/*++ Copyright (c) 1996 Microsoft Corporation Module Name: vote.c Abstract: Routines for sending global updates to the cluster Author: Sunita Shrivastava(sunitas) 17-Mar-1998 Revision History: --*/ #include "gump.h" /**** @doc EXTERNAL INTERFACES CLUSSVC GUM ****/ /**** @func DWORD | GumSendUpdateOnVote| Allows a the caller to collect votes from all active nodes in the cluster before sending an update. @parm IN GUM_UPDATE_TYPE | UpdateType | The update type that will be sent if the decision call back function returns true. @parn IN DWORD | dwContext | This specifies the context related to the Updatetype that will be sent. @parm IN DWORD | dwInputBufLength | The length of the input buffer passed in via pInputBuffer. @parm IN PVOID | pInputBuffer | A pointer to the input buffer that is passed to all the active nodes based on which they can vote. @parm IN DWORD | dwVoteLength | The length of the vote. Depending on this, an appropriately large buffer is allocated to collect all the votes. @parm IN GUM_VOTE_DECISION_CB | pfnGumDecisionCb | The decision call back function that is invoked once all the votes have been collected. @rdesc Returns a result code. ERROR_SUCCESS on success. @comm @xref ****/ DWORD GumSendUpdateOnVote( IN GUM_UPDATE_TYPE UpdateType, IN DWORD dwContext, //vote type IN DWORD dwInputBufLength, //input data to make judgement upon IN PVOID pInputBuffer, //size of the input data IN DWORD dwVoteLength, IN PGUM_VOTE_DECISION_CB pfnGumDecisionCb, IN PVOID pContext ) { DWORD dwVoteBufSize; BOOL bDidAllActiveNodesVote; DWORD dwNumVotes; DWORD dwStatus; GUM_VOTE_DECISION_CONTEXT GumDecisionContext; PBYTE pVoteBuffer=NULL; DWORD dwSequence; DWORD dwDecisionStatus; DWORD dwUpdateBufLength; PBYTE pUpdateBuf=NULL; ClRtlLogPrint(LOG_NOISE, "[GUM] GumSendUpdateOnVote: Type=%1!u! Context=%2!u!\n", UpdateType, dwContext); if (dwVoteLength == 0) { dwStatus = ERROR_INVALID_PARAMETER; goto FnExit; } //SS: We dont have to deal with a join happening between // the time we allocate the buffer to the time we collect votes // this is because the buffer we allocate is big enough to // collect all votes from the maximum number of allowed nodes // of a cluster. dwVoteBufSize = (DWORD)(NmMaxNodes * (sizeof(GUM_VOTE_ENTRY) + dwVoteLength)); //allocate a buffer big enough to collect every bodies pVoteBuffer = (PBYTE)LocalAlloc(LMEM_FIXED, dwVoteBufSize); if (!pVoteBuffer) { dwStatus = GetLastError(); goto FnExit; } ZeroMemory(pVoteBuffer, dwVoteBufSize); //setup the decision context structure GumDecisionContext.UpdateType = UpdateType; GumDecisionContext.dwContext = dwContext; GumDecisionContext.dwInputBufLength = dwInputBufLength; GumDecisionContext.pInputBuf = pInputBuffer; GumDecisionContext.dwVoteLength = dwVoteLength; GumDecisionContext.pContext = pContext; Retry: //gum get sequence dwSequence = GumpSequence; ClRtlLogPrint(LOG_NOISE, "[GUM] GumSendUpdateOnVote: Collect Vote at Sequence=%1!u!\n", dwSequence); //gets the information from all nodes //this is done without acquiring the gum lock //could have been done in parallel if we had the appropriate //networking constructs dwStatus = GumpCollectVotes(&GumDecisionContext, dwVoteBufSize, pVoteBuffer, &dwNumVotes, &bDidAllActiveNodesVote); if (dwStatus != ERROR_SUCCESS) { goto FnExit; } //Call the callback dwDecisionStatus = (*pfnGumDecisionCb)(&GumDecisionContext, dwVoteBufSize, pVoteBuffer, dwNumVotes, bDidAllActiveNodesVote, &dwUpdateBufLength, &pUpdateBuf); ClRtlLogPrint(LOG_NOISE, "[GUM] GumSendUpdateOnVote: Decision Routine returns=%1!u!\n", dwDecisionStatus); if (dwDecisionStatus == ERROR_SUCCESS) { //send the update to the locker node dwStatus = GumAttemptUpdate(dwSequence, UpdateType, dwContext, dwUpdateBufLength, pUpdateBuf); if (dwStatus == ERROR_CLUSTER_DATABASE_SEQMISMATCH || dwStatus == ERROR_REVISION_MISMATCH ) // for mixed mode { //free the update buffer if (pUpdateBuf) { LocalFree(pUpdateBuf); pUpdateBuf = NULL; } goto Retry; } } FnExit: ClRtlLogPrint(LOG_NOISE, "[GUM] GumSendUpdateOnVote: Returning status=%1!u!\n", dwStatus); //free the buffer allocated for vote collection if (pVoteBuffer) { LocalFree(pVoteBuffer); } //free the buffer for update allocated by the decision callback function if (pUpdateBuf) { LocalFree(pUpdateBuf); } return(dwStatus); } /**** @func DWORD | GumCollectVotes| Calls all the nodes in the node to collect their votes. @parm IN PGUM_VOTE_DECISION_CONTEXT | pVoteContext| A pointer to the vote context structure. This describes the type/context/input data for the vote. @parn IN DWORD | dwVoteBufSize| The size of the buffer pointed to by pVoteBuf. @parm OUT PVOID | pVoteBuffer | A pointer to the buffer allocated to collect the votes/data from all the nodes of the cluster. @parm OUT LPDWORD| pdwNumVotes| The number of nodes whose votes were collected. @parm IN BOOL| *pbDidAllActiveNodesVote | A pointer to a BOOL. This is set to true if all active nodes at the time the vote was collected vote. @rdesc Returns a result code. ERROR_SUCCESS on success. @comm This is called by GumSendUpdateOnVote() to collect votes from all the nodes of the cluster. @xref ****/ DWORD GumpCollectVotes( IN PGUM_VOTE_DECISION_CONTEXT pVoteContext, IN DWORD dwVoteBufSize, OUT PBYTE pVoteBuffer, OUT LPDWORD pdwNumVotes, OUT BOOL *pbDidAllActiveNodesVote ) { DWORD dwStatus = ERROR_SUCCESS; DWORD dwVoteStatus = ERROR_SUCCESS; DWORD dwCount = 0; DWORD i; PGUM_VOTE_ENTRY pGumVoteEntry; PGUM_INFO GumInfo; DWORD MyNodeId; *pbDidAllActiveNodesVote = TRUE; GumInfo = &GumTable[pVoteContext->UpdateType]; MyNodeId = NmGetNodeId(NmLocalNode); for (i=ClusterMinNodeId; i <= NmMaxNodeId; i++) { if (GumInfo->ActiveNode[i]) { pGumVoteEntry = (PGUM_VOTE_ENTRY)(pVoteBuffer + (dwCount * (sizeof(GUM_VOTE_ENTRY) + pVoteContext->dwVoteLength))); CL_ASSERT((PBYTE)pGumVoteEntry <= (pVoteBuffer + dwVoteBufSize - sizeof(GUM_VOTE_ENTRY))); // // Dispatch the vote to the specified node. // ClRtlLogPrint(LOG_NOISE, "[GUM] GumVoteUpdate: Dispatching vote type %1!u!\tcontext %2!u! to node %3!d!\n", pVoteContext->UpdateType, pVoteContext->dwContext, i); if (i == MyNodeId) { dwVoteStatus = GumpDispatchVote(pVoteContext->UpdateType, pVoteContext->dwContext, pVoteContext->dwInputBufLength, pVoteContext->pInputBuf, pVoteContext->dwVoteLength, (PBYTE)pGumVoteEntry + sizeof(GUM_VOTE_ENTRY)); } else { GumpStartRpc(i); dwVoteStatus = GumCollectVoteFromNode(GumpRpcBindings[i], pVoteContext->UpdateType, pVoteContext->dwContext, pVoteContext->dwInputBufLength, pVoteContext->pInputBuf, pVoteContext->dwVoteLength, (PBYTE)pGumVoteEntry + sizeof(GUM_VOTE_ENTRY)); GumpEndRpc(i); } if (dwVoteStatus == ERROR_SUCCESS) { pGumVoteEntry->dwFlags = GUM_VOTE_VALID; pGumVoteEntry->dwNodeId = i; pGumVoteEntry->dwNumBytes = pVoteContext->dwVoteLength; dwCount++; } else pbDidAllActiveNodesVote = FALSE; } } *pdwNumVotes = dwCount; return(dwStatus); } /**** @func DWORD | GumpDispatchVote| The routine calls the vote routine registered for a given update type to collect vote for the supplied context and the input data. @parm IN GUM_UPDATE_TYPE | Type | The update type for which this vote is requested. @parn IN DWORD | dwContext | This specifies the context related to the Updatetype for which a vote is being seeked. @parm IN DWORD | dwInputBufLength | The length of the input buffer passed in via pInputBuffer. @parm IN PVOID | pInputBuffer | A pointer to the input buffer via which the input data for the vote is supplied. @parm IN DWORD | dwVoteLength | The length of the vote. This is also the size of the buffer to which pBuf points to. @parm OUT PUCHAR | pVoteBuf| A pointer to a buffer in which this node may cast its vote. The length of the vote must not exceed dwVoteLength. @rdesc Returns a result code. ERROR_SUCCESS on success. @comm @xref ****/ DWORD WINAPI GumpDispatchVote( IN GUM_UPDATE_TYPE Type, IN DWORD dwContext, IN DWORD dwInputBufLength, IN PUCHAR pInputBuf, IN DWORD dwVoteLength, OUT PUCHAR pVoteBuf ) { PGUM_INFO GumInfo; PGUM_RECEIVER Receiver; DWORD Status = ERROR_REQUEST_ABORTED; GumInfo = &GumTable[Type]; if (GumInfo->Joined) { Receiver = GumInfo->Receivers; if (Receiver != NULL) { try { if (Receiver->VoteRoutine) { Status =(*(Receiver->VoteRoutine))(dwContext, dwInputBufLength, pInputBuf, dwVoteLength, pVoteBuf); } } except (CL_UNEXPECTED_ERROR(GetExceptionCode()), EXCEPTION_EXECUTE_HANDLER ) { Status = GetExceptionCode(); } if (Status != ERROR_SUCCESS) { ClRtlLogPrint(LOG_CRITICAL, "[GUM] Vote routine %1!d! failed with status %2!d!\n", Receiver->VoteRoutine, Status); } } } return(Status); }