windows-nt/Source/XPSP1/NT/base/cluster/service/gum/vote.c
2020-09-26 16:20:57 +08:00

376 lines
12 KiB
C

/*++
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 <f GumpCollectVotes>
****/
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 <f GumSendUpdateOnVote> <f GumpDispatchVote>
****/
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 <f GumpCollectVote> <f s_GumCollectVoteFromNode>
****/
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);
}