376 lines
12 KiB
C
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);
|
|
}
|
|
|