mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1290 lines
35 KiB
1290 lines
35 KiB
/*++
|
|
|
|
Copyright (c) 1996 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
receive.c
|
|
|
|
Abstract:
|
|
|
|
Routines for registering for global updates and dispensing
|
|
received global updates to the routines that have registered
|
|
for them.
|
|
|
|
Author:
|
|
|
|
John Vert (jvert) 17-Apr-1996
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
#include "gump.h"
|
|
|
|
|
|
|
|
VOID
|
|
GumReceiveUpdates(
|
|
IN BOOL IsJoining,
|
|
IN GUM_UPDATE_TYPE UpdateType,
|
|
IN PGUM_UPDATE_ROUTINE UpdateRoutine,
|
|
IN PGUM_LOG_ROUTINE LogRoutine,
|
|
IN DWORD DispatchCount,
|
|
IN OPTIONAL PGUM_DISPATCH_ENTRY DispatchTable,
|
|
IN OPTIONAL PGUM_VOTE_ROUTINE VoteRoutine
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Registers a handler for a particular global update type.
|
|
|
|
Arguments:
|
|
|
|
IsJoining - TRUE if the current node is joining. If this is true,
|
|
updates will not be delivered until GumEndJoinUpdate has
|
|
completed successfully. If this is FALSE, updates will be
|
|
delivered immediately.
|
|
|
|
UpdateType - Supplies the update type to register for.
|
|
|
|
UpdateRoutine - Supplies the routine to be called when a global update
|
|
of the specified type occurs.
|
|
|
|
LogRoutine - If supplied, it specifies the logging routine that must be called to
|
|
log transaction to the quorum logs.
|
|
|
|
DispatchCount - Supplies the number of entries in the dispatch table.
|
|
This can be zero.
|
|
|
|
DispatchTable - Supplies a pointer to the dispatch table. If this is
|
|
NULL, no updates of this type will be automatically dispatched.
|
|
|
|
VoteRoutine - If supplied, this specifies the routine to be called when
|
|
a vote for this update type is requested.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PGUM_RECEIVER Receiver;
|
|
|
|
CL_ASSERT(UpdateType < GumUpdateMaximum);
|
|
|
|
Receiver = LocalAlloc(LMEM_FIXED, sizeof(GUM_RECEIVER));
|
|
if (Receiver == NULL) {
|
|
CL_LOGFAILURE(ERROR_NOT_ENOUGH_MEMORY);
|
|
return;
|
|
}
|
|
|
|
Receiver->UpdateRoutine = UpdateRoutine;
|
|
Receiver->LogRoutine = LogRoutine;
|
|
Receiver->DispatchCount = DispatchCount;
|
|
Receiver->DispatchTable = DispatchTable;
|
|
Receiver->VoteRoutine = VoteRoutine;
|
|
//
|
|
// John Vert (jvert) 8/2/1996
|
|
// remove below debug print if we ever want to support
|
|
// multiple GUM handlers.
|
|
//
|
|
if (GumTable[UpdateType].Receivers != NULL) {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[GUM] Multiple GUM handlers registered for UpdateType %1!d!\n",
|
|
UpdateType);
|
|
}
|
|
|
|
EnterCriticalSection(&GumpLock);
|
|
Receiver->Next = GumTable[UpdateType].Receivers;
|
|
GumTable[UpdateType].Receivers = Receiver;
|
|
if (IsJoining) {
|
|
GumTable[UpdateType].Joined = FALSE;
|
|
} else {
|
|
GumTable[UpdateType].Joined = TRUE;
|
|
}
|
|
LeaveCriticalSection(&GumpLock);
|
|
}
|
|
|
|
|
|
VOID
|
|
GumIgnoreUpdates(
|
|
IN GUM_UPDATE_TYPE UpdateType,
|
|
IN PGUM_UPDATE_ROUTINE UpdateRoutine
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Removes an update handler from the GUM table. This is the opposite
|
|
of GumReceiveUpdates
|
|
|
|
Arguments:
|
|
|
|
UpdateType - Supplies the update type to register for.
|
|
|
|
UpdateRoutine - Supplies the routine to be called when a global update
|
|
of the specified type occurs.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
PGUM_RECEIVER Receiver;
|
|
PGUM_RECEIVER *Last;
|
|
|
|
//
|
|
// We cannot safely de-registr from Gum... ASSERT if anyone calls this
|
|
// function.
|
|
//
|
|
CL_ASSERT(FALSE);
|
|
|
|
//
|
|
// Walk the list of receivers until we find the specified UpdateRoutine
|
|
//
|
|
Last = &GumTable[UpdateType].Receivers;
|
|
EnterCriticalSection(&GumpLock);
|
|
while ((Receiver = *Last) != NULL) {
|
|
if (Receiver->UpdateRoutine == UpdateRoutine) {
|
|
*Last = Receiver->Next;
|
|
break;
|
|
}
|
|
Last = &Receiver->Next;
|
|
}
|
|
LeaveCriticalSection(&GumpLock);
|
|
if (Receiver != NULL) {
|
|
LocalFree(Receiver);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
DWORD
|
|
WINAPI
|
|
GumpDispatchUpdate(
|
|
IN GUM_UPDATE_TYPE Type,
|
|
IN DWORD Context,
|
|
IN BOOL IsLocker,
|
|
IN BOOL SourceNode,
|
|
IN DWORD BufferLength,
|
|
IN PUCHAR Buffer
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Dispatches a GUM update to all the registered handlers on this node
|
|
|
|
Arguments:
|
|
|
|
Sequence - Supplies the GUM sequence number for the update
|
|
|
|
Type - Supplies the GUM_UPDATE_TYPE for the update
|
|
|
|
Context - Supplies a DWORD of context to be passed to the
|
|
GUM update handlers
|
|
|
|
IsLocker - Specifies if this is a locker node.
|
|
|
|
SourceNode - Specifies whether the update originated on this node or not.
|
|
|
|
BufferLength - Supplies the length of the update data
|
|
|
|
Buffer - Supplies a pointer to the update data
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful
|
|
|
|
Win32 error otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
PGUM_INFO GumInfo;
|
|
PGUM_RECEIVER Receiver;
|
|
DWORD Status = ERROR_SUCCESS;
|
|
PGUM_DISPATCH_ENTRY Dispatch;
|
|
|
|
GumInfo = &GumTable[Type];
|
|
|
|
if (GumInfo->Joined) {
|
|
Receiver = GumInfo->Receivers;
|
|
while (Receiver != NULL) {
|
|
if (Receiver->LogRoutine) {
|
|
Status = (*(Receiver->LogRoutine))(PRE_GUM_DISPATCH, GumpSequence,
|
|
Context, Buffer, BufferLength);
|
|
if (Status != ERROR_SUCCESS)
|
|
{
|
|
return(Status);
|
|
}
|
|
}
|
|
|
|
try {
|
|
if ((Receiver->DispatchTable == NULL) ||
|
|
(Receiver->DispatchCount < Context) ||
|
|
(Receiver->DispatchTable[Context].Dispatch1 == NULL)) {
|
|
Status = (Receiver->UpdateRoutine)(Context,
|
|
SourceNode,
|
|
BufferLength,
|
|
Buffer);
|
|
} else {
|
|
Dispatch = &Receiver->DispatchTable[Context];
|
|
//
|
|
// This update should be unmarshalled and dispatched to the
|
|
// appropriate dispatch routine. The format generated by
|
|
// GumpMarshallArgs is an array of offsets into the buffer,
|
|
// followed by the actual args. The dispatch table is
|
|
// responsible for recording the number of arguments.
|
|
//
|
|
CL_ASSERT(Dispatch->ArgCount <= GUM_MAX_DISPATCH_ARGS);
|
|
CL_ASSERT(Dispatch->ArgCount != 0);
|
|
switch (Dispatch->ArgCount) {
|
|
case 1:
|
|
Status = (Dispatch->Dispatch1)(SourceNode,
|
|
GET_ARG(Buffer,0));
|
|
break;
|
|
case 2:
|
|
Status = (Dispatch->Dispatch2)(SourceNode,
|
|
GET_ARG(Buffer,0),
|
|
GET_ARG(Buffer,1));
|
|
break;
|
|
case 3:
|
|
Status = (Dispatch->Dispatch3)(SourceNode,
|
|
GET_ARG(Buffer,0),
|
|
GET_ARG(Buffer,1),
|
|
GET_ARG(Buffer,2));
|
|
break;
|
|
case 4:
|
|
Status = (Dispatch->Dispatch4)(SourceNode,
|
|
GET_ARG(Buffer,0),
|
|
GET_ARG(Buffer,1),
|
|
GET_ARG(Buffer,2),
|
|
GET_ARG(Buffer,3));
|
|
break;
|
|
case 5:
|
|
Status = (Dispatch->Dispatch5)(SourceNode,
|
|
GET_ARG(Buffer,0),
|
|
GET_ARG(Buffer,1),
|
|
GET_ARG(Buffer,2),
|
|
GET_ARG(Buffer,3),
|
|
GET_ARG(Buffer,4));
|
|
break;
|
|
default:
|
|
CL_ASSERT(FALSE);
|
|
}
|
|
}
|
|
} except (CL_UNEXPECTED_ERROR(GetExceptionCode()),
|
|
EXCEPTION_EXECUTE_HANDLER
|
|
)
|
|
{
|
|
Status = GetExceptionCode();
|
|
}
|
|
if (Status != ERROR_SUCCESS) {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[GUM] Update routine %1!d! failed with status %2!d!\n",
|
|
Receiver->UpdateRoutine,
|
|
Status);
|
|
break;
|
|
}
|
|
|
|
if (Receiver->LogRoutine) {
|
|
if (IsLocker && (Status == ERROR_SUCCESS))
|
|
(*(Receiver->LogRoutine))(POST_GUM_DISPATCH, GumpSequence,
|
|
Context, Buffer, BufferLength);
|
|
if (!IsLocker)
|
|
(*(Receiver->LogRoutine))(POST_GUM_DISPATCH, GumpSequence,
|
|
Context, Buffer, BufferLength);
|
|
}
|
|
Receiver = Receiver->Next;
|
|
|
|
}
|
|
}
|
|
|
|
if (Status == ERROR_SUCCESS) {
|
|
GumpSequence += 1;
|
|
}
|
|
return(Status);
|
|
}
|
|
|
|
//rod wants to call this a mandatory update instead of H...word
|
|
//some times reupdates get delivered in different views on different
|
|
//nodes causing a problem
|
|
//For instance, a locker node might see an update and complete it
|
|
//successfully in one view but when it replays it in another view
|
|
//other nodes may not be able to complete it successfully and may be
|
|
//banished.
|
|
//in one particular case, the locker node approved of a node join
|
|
//because it had finished the node down processing for that node.
|
|
//subsequently another node and hence the joiner went down.
|
|
//the locker node tried to replay the approval update and banished
|
|
//other nodes that were seeing this update after the joiner the joiner
|
|
//went down for the second time.
|
|
//The correct solution would involve GUM delivering the node down
|
|
//message as a gum update and delivering it in the same order with
|
|
//respect to other messages on all nodes
|
|
//However this will require some restructuring of code which
|
|
//cant be done in this time frame(for dtc) hence we are using
|
|
//this workaround
|
|
//this workaround is safe for gums initiated by the joiner node during
|
|
//the join process
|
|
void GumpIgnoreSomeUpdatesOnReupdate(
|
|
IN DWORD Type,
|
|
IN DWORD Context)
|
|
{
|
|
if ((Type == GumUpdateFailoverManager) &&
|
|
(Context == FmUpdateApproveJoin))
|
|
GumpLastBufferValid = FALSE;
|
|
}
|
|
|
|
|
|
error_status_t
|
|
s_GumUpdateNode(
|
|
IN handle_t IDL_handle,
|
|
IN DWORD Type,
|
|
IN DWORD Context,
|
|
IN DWORD Sequence,
|
|
IN DWORD BufferLength,
|
|
IN UCHAR Buffer[]
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Server side routine for GumUpdateNode. This is the side that
|
|
receives the update and dispatches it to the appropriate
|
|
handlers.
|
|
|
|
Arguments:
|
|
|
|
IDL_handle - RPC binding handle, not used
|
|
|
|
Type - Supplies the GUM_UPDATE_TYPE
|
|
|
|
Context - Supplies a DWORD of context to be passed to the
|
|
GUM update handlers
|
|
|
|
Sequence - Supplies the GUM sequence number for the specified update type
|
|
|
|
BufferLength - Supplies the length of the update data
|
|
|
|
Buffer - Supplies a pointer to the update data.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if the update completed successfully
|
|
|
|
ERROR_CLUSTER_DATABASE_SEQMISMATCH if the GUM sequence number is invalid
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD Status;
|
|
PGUM_INFO GumInfo;
|
|
|
|
//
|
|
// We need to grap the gumsendupdate lock to serialize send/replay
|
|
//
|
|
EnterCriticalSection(&GumpSendUpdateLock);
|
|
GumInfo = &GumTable[Type];
|
|
if (Sequence != GumpSequence) {
|
|
|
|
MIDL_user_free(Buffer);
|
|
if (Sequence+1 == GumpSequence) {
|
|
//
|
|
// This is a duplicate of a previously seen update, probably due to
|
|
// a node failure during GUM. Return success since we have already done
|
|
// this.
|
|
//
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[GUM] s_GumUpdateNode: Sequence %1!u! is a duplicate of last sequence for Type %2!u!\n",
|
|
Sequence,
|
|
Type);
|
|
|
|
LeaveCriticalSection(&GumpSendUpdateLock);
|
|
return(ERROR_SUCCESS);
|
|
} else {
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[GUM] s_GumUpdateNode: Sequence %1!u! does not match current %2!u! for Type %3!u!\n",
|
|
Sequence,
|
|
GumpSequence,
|
|
Type);
|
|
LeaveCriticalSection(&GumpSendUpdateLock);
|
|
//
|
|
// [GorN] 10/07/1999. The following code will allow the test program
|
|
// to recognize this sitiation and to restart clustering service
|
|
//
|
|
if( NmGetExtendedNodeState( NmLocalNode ) != ClusterNodeUp){
|
|
CsInconsistencyHalt(ERROR_CLUSTER_DATABASE_SEQMISMATCH);
|
|
}
|
|
return(ERROR_CLUSTER_DATABASE_SEQMISMATCH);
|
|
}
|
|
}
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[GUM] s_GumUpdateNode: dispatching seq %1!u!\ttype %2!u! context %3!u!\n",
|
|
Sequence,
|
|
Type,
|
|
Context);
|
|
//SS: set IsLocker to FALSE,
|
|
Status = GumpDispatchUpdate(Type,
|
|
Context,
|
|
FALSE,
|
|
FALSE,
|
|
BufferLength,
|
|
Buffer);
|
|
if (Status != ERROR_SUCCESS) {
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[GUM] Cluster state inconsistency check\n");
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[GUM] s_GumUpdateNode update routine type %1!u! context %2!d! failed with error %3!d! on non-locker node\n",
|
|
Type,
|
|
Context,
|
|
Status);
|
|
CL_UNEXPECTED_ERROR( Status );
|
|
MIDL_user_free(Buffer);
|
|
LeaveCriticalSection(&GumpSendUpdateLock);
|
|
return(Status);
|
|
|
|
}
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[GUM] s_GumUpdateNode: completed update seq %1!u!\ttype %2!u! context %3!u!\n",
|
|
Sequence,
|
|
Type,
|
|
Context);
|
|
if (GumpLastBuffer != NULL) {
|
|
MIDL_user_free(GumpLastBuffer);
|
|
}
|
|
GumpLastBuffer = Buffer;
|
|
GumpLastContext = Context;
|
|
GumpLastBufferLength = BufferLength;
|
|
GumpLastUpdateType = Type;
|
|
GumpLastBufferValid = TRUE;
|
|
|
|
GumpIgnoreSomeUpdatesOnReupdate(GumpLastUpdateType, GumpLastContext);
|
|
|
|
LeaveCriticalSection(&GumpSendUpdateLock);
|
|
return(Status);
|
|
}
|
|
|
|
|
|
error_status_t
|
|
s_GumGetNodeSequence(
|
|
IN handle_t IDL_handle,
|
|
IN DWORD Type,
|
|
OUT LPDWORD Sequence,
|
|
OUT LPDWORD LockerNodeId,
|
|
OUT PGUM_NODE_LIST *ReturnNodeList
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns the node's current GUM sequence number for the specified type
|
|
|
|
Arguments:
|
|
|
|
IDL_handle - Supplies the RPC binding handle, not used
|
|
|
|
Type - Supplies the GUM_UPDATE_TYPE
|
|
|
|
Sequence - Returns the sequence number for the specified GUM_UPDATE_TYPE
|
|
|
|
LockerNodeId - Returns the current locker node
|
|
|
|
ReturnNodeList - Returns the list of active nodes
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD i;
|
|
DWORD NodeCount;
|
|
PGUM_INFO GumInfo;
|
|
PGUM_NODE_LIST NodeList;
|
|
|
|
CL_ASSERT(Type < GumUpdateMaximum);
|
|
GumInfo = &GumTable[Type];
|
|
|
|
NodeCount = 0;
|
|
*Sequence = 0; // In case of failure set sequence to 0
|
|
|
|
EnterCriticalSection(&GumpUpdateLock);
|
|
|
|
//
|
|
// Count up the number of nodes in the list.
|
|
//
|
|
for (i=ClusterMinNodeId; i <= NmMaxNodeId; i++) {
|
|
if (GumInfo->ActiveNode[i] == TRUE) {
|
|
++NodeCount;
|
|
}
|
|
}
|
|
CL_ASSERT(NodeCount > 0); // must be at least us in the list.
|
|
|
|
//
|
|
// Allocate node list
|
|
//
|
|
NodeList = MIDL_user_allocate(sizeof(GUM_NODE_LIST) + (NodeCount-1)*sizeof(DWORD));
|
|
if (NodeList == NULL) {
|
|
LeaveCriticalSection(&GumpUpdateLock);
|
|
return(ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
NodeList->NodeCount = NodeCount;
|
|
NodeCount = 0;
|
|
|
|
//
|
|
// Fill in the node id array to be returned.
|
|
//
|
|
for (i=ClusterMinNodeId; i <= NmMaxNodeId; i++) {
|
|
if (GumInfo->ActiveNode[i] == TRUE) {
|
|
NodeList->NodeId[NodeCount] = i;
|
|
++NodeCount;
|
|
}
|
|
}
|
|
|
|
*ReturnNodeList = NodeList;
|
|
*Sequence = GumpSequence;
|
|
*LockerNodeId = GumpLockerNode;
|
|
|
|
LeaveCriticalSection(&GumpUpdateLock);
|
|
|
|
return(ERROR_SUCCESS);
|
|
}
|
|
|
|
|
|
error_status_t
|
|
s_GumQueueLockingUpdate(
|
|
IN handle_t IDL_handle,
|
|
IN DWORD NodeId,
|
|
IN DWORD Type,
|
|
IN DWORD Context,
|
|
OUT LPDWORD Sequence,
|
|
IN DWORD BufferLength,
|
|
IN UCHAR Buffer[]
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Queues a locking update. When the lock can be acquired, the update will
|
|
be issued and this routine will return with the lock held.
|
|
|
|
Arguments:
|
|
|
|
IDL_handle - Supplies the RPC binding context, not used.
|
|
|
|
NodeId - Supplies the node id of the issuing node.
|
|
|
|
Type - Supplies the GUM_UPDATE_TYPE of the update
|
|
|
|
Context - Supplies the GUM update context
|
|
|
|
IsLocker - is this is the locker node
|
|
|
|
Sequence - Returns the sequence that the GUM update must be issued with
|
|
|
|
BufferLength - Supplies the length of the update.
|
|
|
|
Buffer - Supplies the update data.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful
|
|
|
|
Win32 error otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD Status;
|
|
PGUM_INFO GumInfo;
|
|
DWORD dwGennum;
|
|
|
|
GumInfo = &GumTable[Type];
|
|
|
|
//
|
|
// Get current node generation number
|
|
//
|
|
dwGennum = GumpGetNodeGenNum(GumInfo, NodeId);
|
|
|
|
|
|
Status = GumpDoLockingUpdate(Type, NodeId, Sequence);
|
|
if (Status != ERROR_SUCCESS) {
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[GUM] s_GumQueueLockingUpdate: GumpDoLockingUpdate failed %1!u!\n",
|
|
Status);
|
|
MIDL_user_free(Buffer);
|
|
return(Status);
|
|
}
|
|
|
|
//
|
|
// If the node that is granted ownership is no longer a member of the
|
|
// cluster or the remote node went down and came back up again, give it up.
|
|
//
|
|
if (GumpDispatchStart(NodeId, dwGennum) != TRUE)
|
|
{
|
|
//skip the dispatch and unlock the lock
|
|
ClRtlLogPrint(LOG_CRITICAL,
|
|
"[GUM] s_GumQueueLockingUpdate: The new locker %1!u! no longer belongs to the cluster\n",
|
|
NodeId);
|
|
Status = ERROR_CLUSTER_NODE_NOT_READY;
|
|
|
|
//
|
|
// Note we have to use Sequence-1 for the unlock because GumpDispatchUpdate
|
|
// failed and did not increment the sequence number.
|
|
//
|
|
GumpDoUnlockingUpdate(Type, *Sequence - 1);
|
|
MIDL_user_free(Buffer);
|
|
return(Status);
|
|
|
|
}
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[GUM] s_GumQueueLockingUpdate: dispatching seq %1!u!\ttype %2!u! context %3!u!\n",
|
|
*Sequence,
|
|
Type,
|
|
Context);
|
|
//SS: Set IsLocker to TRUE
|
|
Status = GumpDispatchUpdate(Type,
|
|
Context,
|
|
TRUE,
|
|
FALSE,
|
|
BufferLength,
|
|
Buffer);
|
|
|
|
if (Status != ERROR_SUCCESS) {
|
|
//
|
|
// Note we have to use Sequence-1 for the unlock because GumpDispatchUpdate
|
|
// failed and did not increment the sequence number.
|
|
//
|
|
GumpDispatchAbort();
|
|
GumpDoUnlockingUpdate(Type, *Sequence - 1);
|
|
if (Buffer != NULL)
|
|
MIDL_user_free(Buffer);
|
|
} else {
|
|
if (GumpLastBuffer != NULL) {
|
|
MIDL_user_free(GumpLastBuffer);
|
|
}
|
|
GumpLastBuffer = Buffer;
|
|
GumpLastContext = Context;
|
|
GumpLastBufferLength = BufferLength;
|
|
GumpLastUpdateType = Type;
|
|
GumpLastBufferValid = TRUE;
|
|
GumpIgnoreSomeUpdatesOnReupdate(GumpLastUpdateType, GumpLastContext);
|
|
//
|
|
// Just in case our client dies
|
|
//
|
|
GumpDispatchEnd(NodeId, dwGennum);
|
|
}
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[GUM] s_GumQueueLockingUpdate: completed update seq %1!u!\ttype %2!u! context %3!u! result %4!u!\n",
|
|
*Sequence,
|
|
Type,
|
|
Context,
|
|
Status);
|
|
|
|
return(Status);
|
|
|
|
}
|
|
|
|
|
|
#ifdef GUM_POST_SUPPORT
|
|
|
|
John Vert (jvert) 11/18/1996
|
|
POST is disabled for now since nobody uses it.
|
|
N.B. The below code does not handle locker node failures
|
|
|
|
error_status_t
|
|
s_GumQueueLockingPost(
|
|
IN handle_t IDL_handle,
|
|
IN DWORD NodeId,
|
|
IN DWORD Type,
|
|
IN DWORD Context,
|
|
OUT LPDWORD Sequence,
|
|
IN DWORD BufferLength,
|
|
IN UCHAR Buffer[],
|
|
IN DWORD ActualBuffer
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Queues a post update.
|
|
|
|
If the GUM lock can be immediately acquired, this routine
|
|
behaves exactly like GumQueueLockingUpdate and returns
|
|
ERROR_SUCCESS.
|
|
|
|
If the GUM lock is held, this routine queues an asynchronous
|
|
wait block onto the GUM queue and returns ERROR_IO_PENDING.
|
|
When the wait block is removed from the GUM queue, the unlocking
|
|
thread will call GumpDeliverPostUpdate on the specified node
|
|
and supply the passed in context. The calling node can then
|
|
deliver the update.
|
|
|
|
Arguments:
|
|
|
|
IDL_handle - Supplies the RPC binding context, not used.
|
|
|
|
NodeId - Supplies the node id of the issuing node.
|
|
|
|
Type - Supplies the GUM_UPDATE_TYPE of the update
|
|
|
|
Context - Supplies the GUM update context
|
|
|
|
Sequence - Returns the sequence that the GUM update must be issued with
|
|
|
|
BufferLength - Supplies the length of the update.
|
|
|
|
Buffer - Supplies the update data.
|
|
|
|
ActualBuffer - Supplies the value of the pointer to the GUM data on the
|
|
client side. This will be returned to the callback if this update
|
|
is completed asynchronously.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful
|
|
|
|
Win32 error otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD Status;
|
|
|
|
Status = GumpDoLockingPost(Type, NodeId, Sequence, Context, BufferLength,
|
|
ActualBuffer, Buffer);
|
|
if (Status != ERROR_SUCCESS) {
|
|
if (Status != ERROR_IO_PENDING) {
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[GUM] s_GumQueueLockingPost: GumpDoLockingPost failed %1!u!\n",
|
|
Status);
|
|
} else {
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[GUM] s_GumQueueLockingPost: GumpDoLockingPost pended update type %1!u! context %2!u!\n",
|
|
Type,
|
|
Context);
|
|
}
|
|
return(Status);
|
|
}
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[GUM] s_GumQueueLockingPost: dispatching seq %1!u!\ttype %2!u! context %3!u!\n",
|
|
*Sequence,
|
|
Type,
|
|
Context);
|
|
//SS: setting IsLocker to FALSE
|
|
Status = GumpDispatchUpdate(Type,
|
|
Context,
|
|
FALSE,
|
|
FALSE,
|
|
BufferLength,
|
|
Buffer);
|
|
CL_ASSERT(Status == ERROR_SUCCESS); // posts must never fail
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[GUM] s_GumQueueLockingPost: completed update seq %1!u!\ttype %2!u! context %3!u! result %4!u!\n",
|
|
*Sequence,
|
|
Type,
|
|
Context,
|
|
Status);
|
|
MIDL_user_free(Buffer);
|
|
|
|
return(Status);
|
|
|
|
}
|
|
#endif
|
|
|
|
|
|
error_status_t
|
|
s_GumAttemptLockingUpdate(
|
|
IN handle_t IDL_handle,
|
|
IN DWORD NodeId,
|
|
IN DWORD Type,
|
|
IN DWORD Context,
|
|
IN DWORD Sequence,
|
|
IN DWORD BufferLength,
|
|
IN UCHAR Buffer[]
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Attempts a locking update. If the supplied sequence number
|
|
matches and the update lock is not already held, the update
|
|
will be issued and this routine will return with the lock held.
|
|
|
|
Arguments:
|
|
|
|
IDL_handle - Supplies the RPC binding context, not used.
|
|
|
|
NodeId - Supplies the node id of the issuing node.
|
|
|
|
Type - Supplies the GUM_UPDATE_TYPE of the update
|
|
|
|
Context - Supplies the GUM update context
|
|
|
|
Sequence - Supplies the sequence that the GUM update must be issued with
|
|
|
|
BufferLength - Supplies the length of the update.
|
|
|
|
Buffer - Supplies the update data.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful
|
|
|
|
Win32 error otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD Status;
|
|
|
|
if (!GumpTryLockingUpdate(Type, NodeId, Sequence)) {
|
|
MIDL_user_free(Buffer);
|
|
return(ERROR_CLUSTER_DATABASE_SEQMISMATCH);
|
|
}
|
|
|
|
//SS: setting Islocker false
|
|
Status = GumpDispatchUpdate(Type,
|
|
Context,
|
|
FALSE,
|
|
FALSE,
|
|
BufferLength,
|
|
Buffer);
|
|
if (Status != ERROR_SUCCESS) {
|
|
//
|
|
// The update has failed on this node, unlock here
|
|
// Note we have to use Sequence-1 for the unlock because GumpDispatchUpdate
|
|
// failed and did not increment the sequence number.
|
|
//
|
|
GumpDoUnlockingUpdate(Type, Sequence-1);
|
|
}
|
|
|
|
MIDL_user_free(Buffer);
|
|
return(Status);
|
|
|
|
}
|
|
|
|
|
|
error_status_t
|
|
s_GumUnlockUpdate(
|
|
IN handle_t IDL_handle,
|
|
IN DWORD Type,
|
|
IN DWORD Sequence
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Unlocks a locked update.
|
|
|
|
Arguments:
|
|
|
|
IDL_handle - Supplies the RPC binding context, not used.
|
|
|
|
Type - Supplies the GUM_UPDATE_TYPE of the update
|
|
|
|
Sequence - Supplies the sequence that the GUM update was issued with
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful
|
|
|
|
Win32 error code otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
GumpDoUnlockingUpdate(Type, Sequence);
|
|
|
|
return(ERROR_SUCCESS);
|
|
}
|
|
|
|
|
|
error_status_t
|
|
s_GumJoinUpdateNode(
|
|
IN handle_t IDL_handle,
|
|
IN DWORD JoiningId,
|
|
IN DWORD Type,
|
|
IN DWORD Context,
|
|
IN DWORD Sequence,
|
|
IN DWORD BufferLength,
|
|
IN UCHAR Buffer[]
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Server side routine for GumJoinUpdateNode. This is the side that
|
|
receives the update, adds the node to the update list, and dispatches
|
|
it to the appropriate handlers.
|
|
|
|
Arguments:
|
|
|
|
IDL_handle - RPC binding handle, not used
|
|
|
|
JoiningId - Supplies the nodeid of the joining node.
|
|
|
|
Type - Supplies the GUM_UPDATE_TYPE
|
|
|
|
Context - Supplies a DWORD of context to be passed to the
|
|
GUM update handlers
|
|
|
|
Sequence - Supplies the GUM sequence number for the specified update type
|
|
|
|
BufferLength - Supplies the length of the update data
|
|
|
|
Buffer - Supplies a pointer to the update data.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if the update completed successfully
|
|
|
|
ERROR_INVALID_HANDLE if the GUM sequence number is invalid
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD Status;
|
|
PGUM_INFO GumInfo;
|
|
|
|
GumInfo = &GumTable[Type];
|
|
|
|
// sync with replay/updates
|
|
EnterCriticalSection(&GumpSendUpdateLock);
|
|
// [ahm] This is an aborted endjoin, we just resync our seq. with master.
|
|
// This should be its own GumUpdateSequence RPC, but for now it ok to
|
|
// to this.
|
|
if (JoiningId == (DWORD) -1)
|
|
{
|
|
// we must be off by one at the most
|
|
if (Sequence+1 != GumpSequence)
|
|
{
|
|
CL_ASSERT(Sequence == GumpSequence);
|
|
GumpSequence = Sequence+1;
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[GUM] s_GumJoinUpdateNode: pretend we have seen Sequence %1!u!\n",
|
|
Sequence);
|
|
}
|
|
Status = 0;
|
|
goto done;
|
|
}
|
|
|
|
if (Sequence != GumpSequence) {
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[GUM] s_GumJoinUpdateNode: Sequence %1!u! does not match current %2!u! for Type %3!u!\n",
|
|
Sequence,
|
|
GumpSequence,
|
|
Type);
|
|
LeaveCriticalSection(&GumpSendUpdateLock);
|
|
MIDL_user_free(Buffer);
|
|
return(ERROR_CLUSTER_DATABASE_SEQMISMATCH);
|
|
}
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[GUM] s_GumJoinUpdateNode: dispatching seq %1!u!\ttype %2!u! context %3!u!\n",
|
|
Sequence,
|
|
Type,
|
|
Context);
|
|
|
|
CL_ASSERT(NmIsValidNodeId(JoiningId));
|
|
CL_ASSERT(GumpRpcBindings[JoiningId] != NULL);
|
|
CL_ASSERT(GumpReplayRpcBindings[JoiningId] != NULL);
|
|
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[GUM] s_GumJoinUpdateNode Adding node %1!d! to update list for GUM type %2!d!\n",
|
|
JoiningId,
|
|
Type);
|
|
|
|
//SS: setting IsLocker to FALSE
|
|
Status = GumpDispatchUpdate(Type,
|
|
Context,
|
|
FALSE,
|
|
FALSE,
|
|
BufferLength,
|
|
Buffer);
|
|
|
|
// [ahm]: We need to make sure the node is still up, otherwise ignore
|
|
EnterCriticalSection(&GumpLock);
|
|
if (MMIsNodeUp(JoiningId) == TRUE) {
|
|
GumTable[Type].ActiveNode[JoiningId] = TRUE;
|
|
}
|
|
LeaveCriticalSection(&GumpLock);
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[GUM] s_GumJoinUpdateNode: completed update seq %1!u!\ttype %2!u! context %3!u!\n",
|
|
Sequence,
|
|
Type,
|
|
Context);
|
|
|
|
done:
|
|
if (GumpLastBuffer != NULL) {
|
|
MIDL_user_free(GumpLastBuffer);
|
|
}
|
|
|
|
GumpLastBuffer = NULL;
|
|
GumpLastContext = Context;
|
|
GumpLastBufferLength = 0;
|
|
GumpLastUpdateType = Type;
|
|
GumpLastBufferValid = FALSE;
|
|
|
|
LeaveCriticalSection(&GumpSendUpdateLock);
|
|
|
|
MIDL_user_free(Buffer);
|
|
return(Status);
|
|
}
|
|
|
|
|
|
error_status_t
|
|
s_GumAttemptJoinUpdate(
|
|
IN handle_t IDL_handle,
|
|
IN DWORD JoiningId,
|
|
IN DWORD Type,
|
|
IN DWORD Context,
|
|
IN DWORD Sequence,
|
|
IN DWORD BufferLength,
|
|
IN UCHAR Buffer[]
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Attempts a locking join update. If the supplied sequence number
|
|
matches and the update lock is not already held, the join update
|
|
will be issued, the joining node will be added to the update list,
|
|
and this routine will return with the lock held.
|
|
|
|
Arguments:
|
|
|
|
IDL_handle - Supplies the RPC binding context, not used.
|
|
|
|
JoiningId - Supplies the nodeid of the joining node.
|
|
|
|
Type - Supplies the GUM_UPDATE_TYPE of the update
|
|
|
|
Context - Supplies the GUM update context
|
|
|
|
Sequence - Supplies the sequence that the GUM update must be issued with
|
|
|
|
BufferLength - Supplies the length of the update.
|
|
|
|
Buffer - Supplies the update data.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS if successful
|
|
|
|
Win32 error otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD Status;
|
|
PGUM_INFO GumInfo;
|
|
|
|
GumInfo = &GumTable[Type];
|
|
|
|
if (!GumpTryLockingUpdate(Type, JoiningId, Sequence)) {
|
|
MIDL_user_free(Buffer);
|
|
return(ERROR_CLUSTER_DATABASE_SEQMISMATCH);
|
|
}
|
|
|
|
// sync with replay/updates
|
|
EnterCriticalSection(&GumpSendUpdateLock);
|
|
|
|
//SS: set IsLocker to TRUE
|
|
Status = GumpDispatchUpdate(Type,
|
|
Context,
|
|
TRUE,
|
|
FALSE,
|
|
BufferLength,
|
|
Buffer);
|
|
if (Status != ERROR_SUCCESS) {
|
|
//
|
|
// The update has failed on this node, unlock here
|
|
// Note we have to use Sequence-1 for the unlock because
|
|
// GumpDispatchUpdate failed and did not increment the
|
|
// sequence number.
|
|
//
|
|
GumpDoUnlockingUpdate(Type, Sequence-1);
|
|
} else {
|
|
CL_ASSERT(NmIsValidNodeId(JoiningId));
|
|
CL_ASSERT(GumpRpcBindings[JoiningId] != NULL);
|
|
CL_ASSERT(GumpReplayRpcBindings[JoiningId] != NULL);
|
|
|
|
ClRtlLogPrint(LOG_UNUSUAL,
|
|
"[GUM] s_GumAttemptJoinUpdate Adding node %1!d! to update list for GUM type %2!d!\n",
|
|
JoiningId,
|
|
Type);
|
|
|
|
// [ahm]: We need to make sure the node is still up, otherwise ignore
|
|
EnterCriticalSection(&GumpLock);
|
|
if (MMIsNodeUp(JoiningId) == TRUE) {
|
|
GumTable[Type].ActiveNode[JoiningId] = TRUE;
|
|
}
|
|
LeaveCriticalSection(&GumpLock);
|
|
if (GumpLastBuffer != NULL) {
|
|
MIDL_user_free(GumpLastBuffer);
|
|
}
|
|
GumpLastBuffer = NULL;
|
|
GumpLastContext = Context;
|
|
GumpLastBufferLength = 0;
|
|
GumpLastUpdateType = Type;
|
|
GumpLastBufferValid = FALSE;
|
|
}
|
|
LeaveCriticalSection(&GumpSendUpdateLock);
|
|
MIDL_user_free(Buffer);
|
|
|
|
return(Status);
|
|
|
|
}
|
|
|
|
|
|
/****
|
|
@func DWORD | s_GumCollectVoteFromNode| The is the server side
|
|
routine for GumCollectVoteFromNode.
|
|
|
|
@parm IN IDL_handle | RPC binding handle, not used.
|
|
|
|
@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 A node collecting votes invokes this routine to collect a vote
|
|
from the remote node. This routine simply invokes GumpDispatchVote().
|
|
|
|
@xref <f GumpCollectVote> <f GumpDispatchVote>
|
|
****/
|
|
DWORD
|
|
WINAPI
|
|
s_GumCollectVoteFromNode(
|
|
IN handle_t IDL_handle,
|
|
IN DWORD UpdateType,
|
|
IN DWORD dwContext,
|
|
IN DWORD dwInputBufLength,
|
|
IN PUCHAR pInputBuf,
|
|
IN DWORD dwVoteLength,
|
|
OUT PUCHAR pVoteBuf
|
|
)
|
|
{
|
|
DWORD dwStatus;
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[GUM] s_GumCollectVote: collecting vote for type %1!u!\tcontext %2!u!\n",
|
|
UpdateType,
|
|
dwContext);
|
|
|
|
dwStatus = GumpDispatchVote(UpdateType,
|
|
dwContext,
|
|
dwInputBufLength,
|
|
pInputBuf,
|
|
dwVoteLength,
|
|
pVoteBuf);
|
|
|
|
ClRtlLogPrint(LOG_NOISE,
|
|
"[GUM] s_GumCollectVote: completed, VoteStatus=%1!u!\n",
|
|
dwStatus);
|
|
|
|
return(dwStatus);
|
|
}
|
|
|
|
|
|
|
|
#ifdef GUM_POST_SUPPORT
|
|
|
|
John Vert (jvert) 11/18/1996
|
|
POST is disabled for now since nobody uses it.
|
|
|
|
|
|
error_status_t
|
|
s_GumDeliverPostCallback(
|
|
IN handle_t IDL_handle,
|
|
IN DWORD FirstNode,
|
|
IN DWORD Type,
|
|
IN DWORD Context,
|
|
IN DWORD Sequence,
|
|
IN DWORD BufferLength,
|
|
IN DWORD Buffer
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Callback function used to deliver a posted update that was
|
|
queued.
|
|
|
|
Arguments:
|
|
|
|
IDL_handle - Supplies the RPC binding context, not used.
|
|
|
|
FirstNode - Supplies the node ID where the posts should start.
|
|
This is generally the LockerNode+1.
|
|
|
|
Type - Supplies the GUM_UPDATE_TYPE of the update
|
|
|
|
Context - Supplies the GUM update context
|
|
|
|
Sequence - Supplies the sequence that the GUM update must be issued with
|
|
|
|
BufferLength - Supplies the length of the update.
|
|
|
|
Buffer - Supplies the update data.
|
|
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
GumpDeliverPosts(FirstNode,
|
|
Type,
|
|
Sequence,
|
|
Context,
|
|
BufferLength,
|
|
(PVOID)Buffer);
|
|
return(ERROR_SUCCESS);
|
|
}
|
|
|
|
#endif
|
|
|