Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

1225 lines
40 KiB

#ifdef __TANDEM
#pragma columns 79
#pragma page "srgpif.c - T9050 - interface routines for Regroup Module"
#endif
/* @@@ START COPYRIGHT @@@
** Tandem Confidential: Need to Know only
** Copyright (c) 1995, Tandem Computers Incorporated
** Protected as an unpublished work.
** All Rights Reserved.
**
** The computer program listings, specifications, and documentation
** herein are the property of Tandem Computers Incorporated and shall
** not be reproduced, copied, disclosed, or used in whole or in part
** for any reason without the prior express written permission of
** Tandem Computers Incorporated.
**
** @@@ END COPYRIGHT @@@
**/
/*---------------------------------------------------------------------------
* This file (srgpif.c) contains all the external interface routines
* of Regroup.
*---------------------------------------------------------------------------*/
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#include <wrgp.h>
/************************************************************************
* rgp_estimate_memory
* ===================
*
* Description:
*
* Routine to find the number of bytes of memory needed by regroup.
* The only global memory used by Regroup is for the rgp_control structure.
* The caller must allocate and zero out a chunk of this much memory
* and then call rgp_init() with a pointer to this memory.
*
* Parameters:
*
* None
*
* Returns:
*
* int - number of bytes of locked down and initialized (to 0) memory
* needed by Regroup. The memory must be 4-byte aligned.
*
* Algorithm:
*
* Uses the size of the rgp_control_t to calculate the number of
* bytes needed.
*
************************************************************************/
_priv _resident int
RGP_ESTIMATE_MEMORY(void)
{
return(sizeof(rgp_control_t));
}
/************************************************************************
* rgp_init
* ========
*
* Description:
*
* Routine to initialize the global Regroup data structures.
*
* Parameters:
*
* node_t this_node -
* node number of local node; regroup uses bit masks to represent
* nodes in the cluster and starts numbering nodes from 0. The OS
* starts numbering at LOWEST_NODENUM. This transformation is
* maintained in all the regroup interfaces to the OS.
*
* unsigned int num_nodes -
* number of nodes in the configured node number space =
* (largest configured node number - LOWEST_NODENUM + 1).
*
* void *rgp_buffer -
* pointer to a block of locked down memory initialized to 0; this is
* for use by Regroup as its global memory; must be 4-byte aligned
*
* int rgp_buflen -
* length in bytes of the locked down buffer *rgp_buffer; must be equal
* to or greater than the number returned by rgp_estimate_memory()
*
* rgp_msgsys_p rgp_msgsys_p -
* pointer to a common struct used by the message system and
* Regroup to co-ordinate regroup related work
*
* Returns:
*
* void - no return value
*
* Algorithm:
*
* Initializes the Regroup global data structure with default initial
* values and the parameters passed in.
*
************************************************************************/
_priv _resident void
RGP_INIT(node_t this_node, unsigned int num_nodes,
void *rgp_buffer, int rgp_buflen,
rgp_msgsys_p rgp_msgsys_p)
{
this_node = INT_NODE(this_node); /* adjust the node number by the offset */
if ((num_nodes > MAX_CLUSTER_SIZE) ||
(this_node >= (node_t) num_nodes) ||
(rgp_buflen < rgp_estimate_memory()) /* buffer too small */ ||
((ULONG_PTR)rgp_buffer % 4) /* buffer not 4-byte aligned */
)
RGP_ERROR(RGP_INTERNAL_ERROR);
#ifdef NSK
/* In NSK, the caller must set up the global rgp pointer. */
#else
rgp = (rgp_control_t *) rgp_buffer;
#endif /* NSK */
rgp->num_nodes = num_nodes; /* # of nodes configured */
rgp->rgp_msgsys_p = rgp_msgsys_p; /* ptr to struct shared with Msgsys */
rgp->mynode = this_node;
#if defined (NT)
/* Initialize RGP_LOCK, the CRITICALSECTION object that will be used
* to synchronize access within the regroup procedures */
InitializeCriticalSection( &rgp->OS_specific_control.RgpCriticalSection );
#endif
RGP_CLEANUP();
/* We place a bit pattern in the IamAlive packet. This bit
* pattern toggles all the bits.
*/
rgp->iamalive_pkt.testpattern.words[0] = 0x0055FF6D;
rgp->iamalive_pkt.testpattern.words[1] = 0x92CC33E3;
rgp->iamalive_pkt.testpattern.words[2] = 0x718E49F0;
rgp->iamalive_pkt.testpattern.words[3] = 0x92CC33E3;
rgp->iamalive_pkt.testpattern.words[4] = 0x0055FF6D;
rgp->iamalive_pkt.testpattern.words[5] = 0x0055FF6D;
rgp->iamalive_pkt.testpattern.words[6] = 0x92CC33E3;
rgp->iamalive_pkt.testpattern.words[7] = 0x718E49F0;
rgp->iamalive_pkt.testpattern.words[8] = 0x92CC33E3;
rgp->iamalive_pkt.testpattern.words[9] = 0x0055FF6D;
rgp->iamalive_pkt.testpattern.words[10] = 0x55AA55AA;
rgp->iamalive_pkt.testpattern.words[11] = 0x55AA55AA;
rgp->iamalive_pkt.testpattern.words[12] = 0x55AA55AA;
rgp->poison_pkt.pktsubtype = RGP_UNACK_POISON;
rgp_init_OS(); /* OS-specific initializations */
rgp_cleanup_OS(); /* OS-specific cleanup */
/* Trace the call after the data structures have been initialized. */
RGP_TRACE( "RGP Init called ", EXT_NODE(this_node), num_nodes,
PtrToUlong(rgp_buffer), PtrToUlong(rgp_msgsys_p) ); /* TRACE */
}
/**************************************************************************
* rgp_cleanup
* ===========
* Description:
*
* This function cleans up the RGP structure such that this node is
* virtually returned to the state following RGP_INIT and ready to be
* "join"ed into the cluster.
*
* Parameters:
*
* None
*
* Returns:
*
* None
**************************************************************************/
_priv _resident void
RGP_CLEANUP(void)
{
node_t i;
RGP_LOCK;
/* Initialize the state of all possible nodes in the cluster. */
for (i = 0; i < (node_t) rgp->num_nodes; i++)
{
rgp->node_states[i].status = RGP_NODE_DEAD;
rgp->node_states[i].pollstate = AWAITING_IAMALIVE;
rgp->node_states[i].lostHBs = 0;
#if defined( NT )
ClusnetSetNodeMembershipState(NmClusnetHandle,
EXT_NODE( i ),
ClusnetNodeStateDead);
#endif // NT
}
for (i = (node_t)rgp->num_nodes; i < MAX_CLUSTER_SIZE; i++)
{
rgp->node_states[i].status = RGP_NODE_NOT_CONFIGURED;
rgp->node_states[i].pollstate = AWAITING_IAMALIVE;
rgp->node_states[i].lostHBs = 0;
#if defined( NT )
ClusnetSetNodeMembershipState(NmClusnetHandle,
EXT_NODE( i ),
ClusnetNodeStateNotConfigured);
#endif // NT
}
rgp->rgpinfo.version = RGP_VERSION;
rgp->rgpinfo.seqnum = RGP_INITSEQNUM;
rgp->rgpinfo.iamalive_ticks = RGP_IAMALIVE_TICKS;
rgp->rgpinfo.check_ticks = RGP_CHECK_TICKS;
rgp->rgpinfo.Min_Stage1_ticks = RGP_MIN_STAGE1_TICKS;
rgp->rgpinfo.a_tick = RGP_INACTIVE_PERIOD;
ClusterInit(rgp->rgpinfo.cluster);
rgp->rgppkt.stage = RGP_COLDLOADED;
rgp->rgpcounter = 0;
rgp->restartcount = 0;
rgp->tiebreaker = rgp->mynode;
/* Initialize the unacknowledged packet buffers */
rgp->rgppkt.pktsubtype = RGP_UNACK_REGROUP;
rgp->rgppkt.seqno = rgp->rgpinfo.seqnum;
rgp->last_stable_seqno = rgp->rgpinfo.seqnum;
ClusterCopy(rgp->OS_specific_control.CPUUPMASK, rgp->rgpinfo.cluster);
ClusterCopy(rgp->outerscreen, rgp->rgpinfo.cluster);
#if defined( NT )
ClusnetSetOuterscreen( NmClusnetHandle, (ULONG)*((PUSHORT)rgp->outerscreen) );
#endif
ClusterCopy(rgp->innerscreen, rgp->rgpinfo.cluster);
ClusterCopy(rgp->rgppkt.knownstage1, rgp->rgpinfo.cluster);
ClusterCopy(rgp->rgppkt.knownstage2, rgp->rgpinfo.cluster);
ClusterCopy(rgp->rgppkt.knownstage3, rgp->rgpinfo.cluster);
ClusterCopy(rgp->rgppkt.knownstage4, rgp->rgpinfo.cluster);
ClusterCopy(rgp->rgppkt.knownstage5, rgp->rgpinfo.cluster);
ClusterCopy(rgp->rgppkt.pruning_result, rgp->rgpinfo.cluster);
MatrixInit(rgp->rgppkt.connectivity_matrix);
rgp->rgppkt_to_send.pktsubtype = RGP_UNACK_REGROUP;
rgp->iamalive_pkt.pktsubtype = RGP_UNACK_IAMALIVE;
RGP_UNLOCK;
}
/***************************************************************************
* rgp_sequence_number
* ===================
* Description:
*
* This function returns the regroup sequence number.
*
* This provides only a subset of the functionality provided by
* rgp_getrgpinfo(), but is a simpler function and has no structure
* parameters, making it easier to call from PTAL.
*
* A regroup incident could be in progress when this routine is
* called.
*
* Parameters:
*
* None
*
* Returns:
*
* uint32 - the current regroup sequence number; this reflects
* how many regroup incidents have happened since
* the system came up. Since one incident can result in
* upto RGP_RESTART_MAX restarts each resulting in the
* sequence # being bumped, this number does not always
* equal the number of regroup incidents.
*
***************************************************************************/
_priv _resident uint32
RGP_SEQUENCE_NUMBER(void)
{
return(rgp->rgpinfo.seqnum);
}
/************************************************************************
* rgp_getrgpinfo
* ==============
*
* Description:
*
* Routine to get Regroup parameters.
*
* Parameters:
*
* rgpinfo_t *rgpinfo - pointer to struct to be filled with Regroup
* parameters.
*
* Returns:
*
* int - 0 if successful; -1 if Regroup is perturbed.
*
* Algorithm:
*
* Copies the rgpinfo struct from the Regroup global memory into the
* struct passed in by the caller.
*
************************************************************************/
_priv _resident int
RGP_GETRGPINFO(rgpinfo_t *rgpinfo)
{
int error = 0;
/* If no rgpinfo structure is passed OR rgp_init() has not been called
* earlier, halt.
*/
if ((rgpinfo == RGP_NULL_PTR) || (rgp == RGP_NULL_PTR))
RGP_ERROR( RGP_INTERNAL_ERROR );
RGP_LOCK;
if (rgp_is_perturbed())
error = -1;
else
/* Copy the rgpinfo structure from regroup's internal struct. */
*rgpinfo = rgp->rgpinfo;
RGP_UNLOCK;
return(error);
}
/************************************************************************
* rgp_setrgpinfo
* ==============
*
* Description:
*
* Routine to set Regroup parameters. This routine is to be called on
* newly booting nodes to set the Regroup parameters to the values
* in the master or reloading node. The parameters to be updated
* include Regroup timing parameters and the cluster membership;
* that is, the current set of nodes in the system.
*
* This routine can also be called on the first node to boot to
* modify the Regroup timing parameters which are set to the default
* values when rgp_init() is called. Such modification has to be done
* before other nodes are added to the system.
*
* Parameters:
*
* rgpinfo_t *rgpinfo - pointer to struct with Regroup parameters to
* be modified.
*
* Returns:
*
* int - 0 if successful; -1 if there is more than one node in the
* cluster. This is to prevent modification of timing parameters
* after the second node is added to the system.
*
* Algorithm:
*
* Copies the contents of the user-passed struct into the one in the
* Regroup global memory and updates related parameters.
*
************************************************************************/
_priv _resident int
RGP_SETRGPINFO(rgpinfo_t *rgpinfo)
{
int error = 0;
node_t i;
/* If no rgpinfo structure is passed OR the version # of the
* structure is not understood OR rgp_init() has not been called,
* halt.
*/
if ((rgpinfo == RGP_NULL_PTR) ||
(rgpinfo->version != RGP_VERSION) ||
(rgp == RGP_NULL_PTR))
RGP_ERROR( RGP_INTERNAL_ERROR );
RGP_LOCK;
/* The following checks must be made before proceeding:
*
* 1. Regroup must not be perturbed.
*
* 2. If rgp_start() has been called (regroup is in the
* RGP_STABILIZED state), only the local node must be in the
* cluster when this routine is called.
*
* 3. If rgp_start() has been called, this routine can be used
* only to modify the timing parameters and not to specify the
* cluster.
*
* If these restrictions are not followed, return -1.
*/
RGP_TRACE( "RGP SetRGPInfo ",
rgpinfo->version, /* TRACE */
rgpinfo->seqnum, /* TRACE */
rgpinfo->iamalive_ticks, /* TRACE */
GetCluster( rgpinfo->cluster ) );/* TRACE */
if ( rgp_is_perturbed() ||
( (rgp->rgppkt.stage == RGP_STABILIZED) &&
( (ClusterNumMembers(rgp->rgpinfo.cluster) > 1) ||
!ClusterCompare(rgp->rgpinfo.cluster,rgpinfo->cluster)
)
)
)
error = -1;
else
{
/* Copy the rgpinfo structure into regroup's internal struct. */
rgp->rgpinfo = *rgpinfo;
/* If iamalive_ticks is set to 0, use the default value instead. */ /*F40:KSK06102.2*/
if (rgpinfo->iamalive_ticks == 0) /*F40:KSK06102.3*/
rgp->rgpinfo.iamalive_ticks = RGP_IAMALIVE_TICKS; /*F40:KSK06102.4*/
/*F40:KSK06102.5*/
if (rgpinfo->check_ticks == 0)
{
rgp->rgpinfo.check_ticks = RGP_CHECK_TICKS;
}
if (rgpinfo->Min_Stage1_ticks == 0)
rgp->rgpinfo.Min_Stage1_ticks =
(rgp->rgpinfo.iamalive_ticks * rgp->rgpinfo.check_ticks);
if (rgpinfo->a_tick == 0)
rgp->rgpinfo.a_tick = RGP_CLOCK_PERIOD;
// Tell Timer thread to restart RGP timer
SetEvent (rgp->OS_specific_control.TimerSignal);
/* The cluster should include the local node even if the cluster
* field in the rgpinfo structure does not include it.
*/
ClusterInsert(rgp->rgpinfo.cluster, rgp->mynode);
/* Copy the sequence number into the regroup packet area. */
rgp->rgppkt.seqno = rgp->rgpinfo.seqnum;
/* If nodes have been added in the cluster field, they must be
* added to all the screens and their status must be set to
* alive.
*/
ClusterCopy(rgp->OS_specific_control.CPUUPMASK, rgp->rgpinfo.cluster);
ClusterCopy(rgp->outerscreen, rgp->rgpinfo.cluster);
#if defined( NT )
ClusnetSetOuterscreen( NmClusnetHandle, (ULONG)*((PUSHORT)rgp->outerscreen) );
ClusterComplement(rgp->ignorescreen, rgp->outerscreen);
#endif
ClusterCopy(rgp->innerscreen, rgp->rgpinfo.cluster);
ClusterCopy(rgp->rgppkt.knownstage1, rgp->rgpinfo.cluster);
ClusterCopy(rgp->rgppkt.knownstage2, rgp->rgpinfo.cluster);
ClusterCopy(rgp->rgppkt.knownstage3, rgp->rgpinfo.cluster);
ClusterCopy(rgp->rgppkt.knownstage4, rgp->rgpinfo.cluster);
ClusterCopy(rgp->rgppkt.knownstage5, rgp->rgpinfo.cluster);
ClusterCopy(rgp->rgppkt.pruning_result, rgp->rgpinfo.cluster);
rgp->tiebreaker = rgp_select_tiebreaker(rgp->rgpinfo.cluster);
for (i = 0; i < (node_t) rgp->num_nodes; i++)
{
if (ClusterMember(rgp->rgpinfo.cluster, i))
{
rgp->node_states[i].pollstate = IAMALIVE_RECEIVED;
rgp->node_states[i].status = RGP_NODE_ALIVE;
#if defined( NT )
ClusnetSetNodeMembershipState(NmClusnetHandle,
EXT_NODE( i ),
ClusnetNodeStateAlive);
#endif // NT
}
}
/* Reset the clock counter so that IamAlives are sent when
* the next timer tick arrives.
*/
rgp->clock_ticks = 0;
}
RGP_UNLOCK;
return(error);
}
/************************************************************************
* rgp_start
* =========
*
* Description:
*
* This routine signals the end of node integration into the cluster.
* The node can now start participating in the Regroup algorithm.
*
* Parameters:
*
* void (*rgp_node_failed)()
* pointer to a routine to be called when a node failure is
* detected.
*
* int (*rgp_select_cluster)()
* pointer to an optional routine to be called when link failures
* cause multiple alternative clusters to be formed. This routine
* should select one from a list of suggested clusters.
*
* Returns:
*
* void - no return value
*
* Algorithm:
*
* Installs the callback routines in the global data structure and
* changes the Regroup state to RGP_STABILIZED.
*
************************************************************************/
_priv _resident void
RGP_START(void (*nodedown_callback)(cluster_t failed_nodes),
int (*select_cluster)(cluster_t cluster_choices[], int num_clusters)
)
{
if (rgp == RGP_NULL_PTR)
RGP_ERROR( RGP_INTERNAL_ERROR );
RGP_LOCK;
RGP_TRACE( "RGP Start called",
rgp->rgppkt.stage, /* TRACE */
PtrToUlong(nodedown_callback), /* TRACE */
PtrToUlong(select_cluster), /* TRACE */
0 ); /* TRACE */
/* Install callback routines for node failure notification and cluster
* selection. If no routine is given by the caller, use default ones.
*/
if (nodedown_callback == RGP_NULL_PTR)
{
#ifdef NSK
/* In NSK, rgp_start() is called from pTAL code and passing routine
* addresses is cumbersome. So, RGP_NULL_PTR is passed and we
* call the routine rgp_node_failed() which must be supplied by
* the message system.
*/
rgp->nodedown_callback = rgp_node_failed; /* hardcoded name */
#else
/* A node down callback routine must be supplied. */
RGP_ERROR( RGP_INTERNAL_ERROR );
#endif /* NSK */
}
else
rgp->nodedown_callback = nodedown_callback;
#if 0
/* The select cluster routine is optional. */
if (select_cluster == RGP_NULL_PTR)
rgp->select_cluster = rgp_select_cluster; /* supplied by regroup */
else
#endif
//
// Calling rgp_select_cluster is
// not a good idea since it doesn't take into the consideration
// quorum owner node.
// If rgp->select_cluster == RGP_NULL_PTR, then srgpsm.c uses
// rgp_select_cluster_ex, that will try to select the group
// that contain the current quorum owner node
rgp->select_cluster = select_cluster;
#if defined(NT)
/* Call the node up callback. This is where the local node gets
* the node up callback for itself coming up. Other nodes call
* the callback, for this node coming up, in rgp_monitor_node.
*/
ClusterInsert(rgp->rgpinfo.cluster, rgp->mynode);
ClusterCopy(rgp->OS_specific_control.CPUUPMASK, rgp->rgpinfo.cluster);
if ( rgp->OS_specific_control.UpDownCallback != RGP_NULL_PTR )
{
(*(rgp->OS_specific_control.UpDownCallback))(
EXT_NODE(rgp->mynode),
NODE_UP
);
}
#endif /* NT */
RGP_UNLOCK;
}
/************************************************************************
* rgp_add_node
* ============
*
* Description:
*
* Called to add a newly booting node to the regroup masks. This prevents
* Regroup from sending poison packets to the new node when it tries to
* contact our node by sending IamAlive messages.
*
* Parameters:
*
* node_t node - node to be added to the recognition masks
*
* Returns:
*
* int - 0 on success and -1 on failure. The routine fails only if a
* regroup incident is in progress.
*
* Algorithm:
*
* The node is added to all the recognition masks and its state is
* changed to RGP_NODE_COMING_UP.
*
************************************************************************/
_priv _resident int
RGP_ADD_NODE(node_t node)
{
int error = 0;
RGP_LOCK;
RGP_TRACE( "RGP Add node ", node, rgp->rgppkt.stage,
GetCluster(rgp->outerscreen), /* TRACE */
GetCluster(rgp->rgpinfo.cluster) ); /* TRACE */
/* Cannot add a node while regroup is perturbed. Return -1 in that case.
* The new node booting should fail due to the regroup incident anyway.
*/
if (rgp_is_perturbed())
error = -1;
else
{
node = INT_NODE(node); /* adjust the node number by the offset */
ClusterInsert(rgp->outerscreen, node);
#if defined( NT )
ClusnetSetOuterscreen( NmClusnetHandle, (ULONG)*((PUSHORT)rgp->outerscreen) );
#endif
ClusterInsert(rgp->innerscreen, node);
ClusterInsert(rgp->rgppkt.knownstage1, node);
ClusterInsert(rgp->rgppkt.knownstage2, node);
ClusterInsert(rgp->rgppkt.knownstage3, node);
ClusterInsert(rgp->rgppkt.knownstage4, node);
ClusterInsert(rgp->rgppkt.knownstage5, node);
ClusterInsert(rgp->rgppkt.pruning_result, node);
rgp->node_states[node].pollstate = AWAITING_IAMALIVE;
rgp->node_states[node].status = RGP_NODE_COMING_UP;
rgp->node_states[node].lostHBs = 0;
#if defined( NT )
ClusterDelete( rgp->OS_specific_control.Banished, node );
//
// Remove joining node from ignore screen
//
ClusterDelete( rgp->ignorescreen, node );
PackIgnoreScreen(&rgp->rgppkt, rgp->ignorescreen);
ClusnetSetNodeMembershipState(NmClusnetHandle,
EXT_NODE( node ),
ClusnetNodeStateJoining);
#endif // NT
}
RGP_UNLOCK;
return(error);
}
/************************************************************************
* rgp_monitor_node
* ================
*
* Description:
*
* Called by all running nodes to change the status of a newly booted node
* to UP. Can be called by the new node also; it is a no-op in this case.
*
* Parameters:
*
* node_t node - number of node being declared up
*
* Returns:
*
* int - 0 on success and -1 on failure. The routine fails only if the
* state of the node is neither RGP_NODE_COMING_UP nor RGP_NODE_ALIVE.
*
* Algorithm:
*
* If the node is marked coming up, its state is changed to
* RGP_NODE_ALIVE. If the node has already been marked up,
* nothing is done.
*
************************************************************************/
_priv _resident int
RGP_MONITOR_NODE(node_t node)
{
int error = 0;
RGP_LOCK;
RGP_TRACE( "RGP Monitor node", node, rgp->rgppkt.stage,
GetCluster(rgp->outerscreen), /* TRACE */
GetCluster(rgp->rgpinfo.cluster) ); /* TRACE */
node = INT_NODE(node); /* adjust the node number by the offset */
/* Accept the request only if the state of the node is COMING_UP or UP. */
if (rgp->node_states[node].status == RGP_NODE_COMING_UP)
{
ClusterInsert(rgp->rgpinfo.cluster, node);
rgp->tiebreaker = rgp_select_tiebreaker(rgp->rgpinfo.cluster);
rgp->node_states[node].pollstate = IAMALIVE_RECEIVED;
rgp->node_states[node].status = RGP_NODE_ALIVE;
#if defined(NT)
ClusterCopy(rgp->OS_specific_control.CPUUPMASK, rgp->rgpinfo.cluster);
ClusnetSetNodeMembershipState(NmClusnetHandle,
EXT_NODE( node ),
ClusnetNodeStateAlive);
/* A node came up. Call the node up callback. */
if ( rgp->OS_specific_control.UpDownCallback != RGP_NULL_PTR )
{
(*(rgp->OS_specific_control.UpDownCallback))(
EXT_NODE(node),
NODE_UP
);
}
#endif /* NT */
}
else if (rgp->node_states[node].status != RGP_NODE_ALIVE)
/* Perhaps the booting node failed and regroup has already marked
* it down. The cluster manager may have invoked a global update
* resulting in this call before regroup reporetd the failure
* of the node.
*/
error = -1;
RGP_UNLOCK;
return(error);
}
/************************************************************************
* rgp_remove_node
* ===============
*
* Description:
*
* Called by the cluster manager to force out a booting node if booting
* fails. Regroup may or may not have already removed the booting node
* from the masks and declared it down, depending on what stage the
* booting is in and when the booting node failed.
*
* Regroup can remove the node from the masks of all nodes in the cluster
* by simply starting a new incident of regroup with any event code. This
* will force all nodes to come to an agreement on cluster membership
* that excludes the booting node. If the booting node is alive, it will
* commit suicide since it will be in the incompetent (RGP_COLDLOADED)
* state.
*
* Removing the new node from our masks is not necessary since regroup
* will detect the node failure and adjust the masks. If we do remove it
* from our masks BEFORE initiating regroup, regroup may complete quicker
* since we will not wait in stage 1 for the node to check in. Also, this
* could allow a node to be removed even after it is fully integrated.
* This is because our node will send a poison packet to the removed node
* if it tries to contact us.
*
* But this "enhancement" is not implemented because it requires a new
* regroup event code which is examined by all nodes and processed
* specially. Currently, the regroup event code is used only for
* debugging info. Also, there is no guarantee that all nodes see the
* same regroup reason code. For instance, some may see a missing
* IamAlive while others may see a power failure.
*
* Parameters:
*
* node_t node - node to be removed from the recognition masks
* (in external format).
*
* Returns:
*
* int - 0 on success and -1 on failure. The routine fails if a
* regroup incident is in progress or rgp_start() has not been
* called (as in a new node where the booting is not complete).
*
* Algorithm:
*
* If the node is still in the recognition masks, a new regroup incident
* is started. This incident will result in all nodes declaring the node
* dead and removing it from the recognition masks.
*
************************************************************************/
_priv _resident int
RGP_REMOVE_NODE(node_t node)
{
int error = 0;
RGP_LOCK;
RGP_TRACE( "RGP Remove node ", node, rgp->rgppkt.stage,
GetCluster(rgp->outerscreen), /* TRACE */
GetCluster(rgp->rgpinfo.cluster) ); /* TRACE */
if (rgp->rgppkt.stage == RGP_STABILIZED)
{
if (ClusterMember(rgp->outerscreen, INT_NODE(node)))
{
/* Node is currently in our screen. The node may have never come up
* after rgp_add_node() was called OR regroup may not have figured
* out yet that the node is down. In either case, the node must
* be forced out and all nodes in the cluster notified (by a regroup
* incident). If the node is still running, it will commit suicide
* when this regroup incident starts.
*/
rgp_event_handler(RGP_EVT_LATEPOLLPACKET, node);
}
else
{
/* Either the node was not added to the cluster OR regroup has
* already figured out that the node is dead and reported this.
* In either case, there is nothing more to do.
*/
}
}
else
error = -1;
RGP_UNLOCK;
return(error);
}
/************************************************************************
* rgp_is_perturbed
* ================
*
* Description:
*
* Function to check if a regroup incident is in progress.
*
* Parameters:
*
* None.
*
* Returns:
*
* int - 0 if no regroup is quiescent; non-zero if a regroup incident
* is in progress.
*
* Algorithm:
*
* Looks at the current state of the Regroup algorithm.
*
************************************************************************/
_priv _resident int
RGP_IS_PERTURBED(void)
{
uint8 stage = rgp->rgppkt.stage;
return((stage != RGP_STABILIZED) && (stage != RGP_COLDLOADED));
}
/************************************************************************
* rgp_periodic_check
* ==================
*
* Description:
*
* This routine is invoked every RGP_CLOCK_PERIOD by the timer interrupt
* handler of the native OS. It performs Regroups's periodic operations.
*
* Parameters:
*
* None
*
* Returns:
*
* void - no return value
*
* Algorithm:
*
* This routine requests Iamalive packets to be sent, checks if
* IamAlives have been received (and calls rgp_event_handler() if
* not) and sends a clock tick to the regroup algorithm if it is in
* progress.
*
* IamAlives are checked at twice the IamAlive period. The regroup
* global variable clock_ticks is incremented in each call. After
* the IamAlives are checked, clock_ticks is reset to 0. Thus, the
* ticker counts time modulo twice the IamAlive ticks.
*
************************************************************************/
_priv _resident void
RGP_PERIODIC_CHECK(void)
{
node_t node;
RGP_LOCK;
/* If regroup is active, give it a shot at each regroup clock tick. */
if ((rgp->rgppkt.stage != RGP_STABILIZED) &&
(rgp->rgppkt.stage != RGP_COLDLOADED))
rgp_event_handler(RGP_EVT_CLOCK_TICK, RGP_NULL_NODE);
#if !defined( NT )
/* Send IamAlive messages at appropriate intervals. */
if ( (rgp->clock_ticks == 0) ||
(rgp->clock_ticks == rgp->rgpinfo.iamalive_ticks) )
{
rgp_broadcast(RGP_UNACK_IAMALIVE);
rgp->clock_ticks++;
}
/* Check for missing IamAlives at IamAlive sending period,
* But flag an error (LATE_POLL) only if "check_ticks" IamAlives missed.
* The checking is offset from the sending by one clock tick.
*/
else if ( rgp->clock_ticks >= (rgp->rgpinfo.iamalive_ticks - 1) )
{ /* check all nodes for IamAlives received */
for (node = 0; node < (node_t) rgp->num_nodes; node++)
{
if (rgp->node_states[node].status == RGP_NODE_ALIVE)
{
if ( rgp->node_states[node].pollstate == IAMALIVE_RECEIVED )
{ /* checked in in time */
#if defined(TDM_DEBUG)
if ( rgp->OS_specific_control.debug.doing_tracing )
{
printf ("Node %d: Node %d is alive. My rgp state=%d\n",
EXT_NODE(rgp->mynode), EXT_NODE(node), rgp->rgppkt.stage );
}
#endif
rgp->node_states[node].pollstate = AWAITING_IAMALIVE;
rgp->node_states[node].lostHBs = 0;
}
else if ( rgp->node_states[node].lostHBs++ < rgp->rgpinfo.check_ticks )
;// allow upto (check_ticks-1) IamAlives to be lost.
else
{
/* missing IamAlives */
if (node == rgp->mynode) /* missed my own packets */
{
/* We should be lenient if we just had a power failure.
*/
if (rgp->pfail_state == 0) /* no recent power failure */
RGP_ERROR( RGP_MISSED_POLL_TO_SELF );
}
else
rgp_event_handler(RGP_EVT_LATEPOLLPACKET, EXT_NODE(node));
}
}
}
/* Reset the regroup tick counter after checking for IamAlives. */
rgp->clock_ticks = 0;
} /* check all nodes for IamAlives received */
else
rgp->clock_ticks++;
/* rgp->pfail_state is set to a non-zero value when a pfail event
* is reported to regroup. It is decremented at every regroup clock
* tick till it reaches zero. While this number is non-zero, missing
* self IamAlives are ignored and do not cause the node to halt.
* This gives the sending hardware some time to recover from power
* failures before self IamAlives are checked.
*/
if (rgp->pfail_state)
rgp->pfail_state--;
#endif // NT
RGP_UNLOCK;
} /* rgp_periodic_check */
/************************************************************************
* rgp_received_packet
* ===================
*
* Description:
*
* Routine to be called by the message system when an unacknowledged
* packet sent by the Regroup module is received from any node. These
* packets include IamAlive packets, regroup status packets and poison
* packets.
*
* Parameters:
*
* node_t node - node from which a packet has been received
*
* void *packet - address of the received packet data
*
* int packetlen - length in bytes of the received packet data
*
* Returns:
*
* void - no return value
*
* Algorithm:
*
* Does different things based on the packet subtype.
*
************************************************************************/
_priv _resident void
RGP_RECEIVED_PACKET(node_t node, void *packet, int packetlen)
{
rgp_unseq_pkt_t *unseq_pkt = (rgp_unseq_pkt_t *) packet;
node = INT_NODE(node);
/* If the packet is from a node that cannot be in our cluster,
* simply ignore it.
*/
if (node >= (node_t) rgp->num_nodes)
return;
/* If the sending node is excluded by the outer screen, then it is
* not part of the current (most recently known) configuration.
* Therefore the packet should not be honored, and a poison message
* should be sent to try to kill this renegade processor unless
* it is sending US a poison packet. If it is sending us a poison
* packet, we cannot send it a poison in return because that results
* in an infinite loop. In this case, we just halt because this
* situation implies that there is a split brain situation and our
* split brain avoidance algorithm has failed.
*/
/* NT Notes
*
* even with poison pkts being sent and recv'ed in the kernel, we still
* want to make these checks since clusnet doesn't have the regroup stage
* info and regroup packets themselves find there way in here.
*/
if (!ClusterMember(rgp->outerscreen, node)
#if defined( NT )
||
ClusterMember(rgp->OS_specific_control.Banished, node)
#endif
)
{
if (rgp->rgppkt.stage == RGP_COLDLOADED)
{
// We are doing this check in srgpsm.c
// No need to do it here
// RGP_ERROR(RGP_RELOADFAILED);
//
}
else if (unseq_pkt->pktsubtype == RGP_UNACK_POISON)
{
RGP_ERROR((uint16) (RGP_PARIAH + EXT_NODE(node)));
} else {
/* Must send a poison packet to the sender.
*/
ClusterInsert(rgp->poison_targets, node);
rgp_broadcast(RGP_UNACK_POISON);
}
return;
}
switch (unseq_pkt->pktsubtype)
{
case RGP_UNACK_IAMALIVE :
{
/* Count the number of IamAlives received */
if ( node == rgp->mynode )
RGP_INCREMENT_COUNTER( RcvdLocalIAmAlive );
else
RGP_INCREMENT_COUNTER( RcvdRemoteIAmAlive );
if (rgp->node_states[node].status == RGP_NODE_ALIVE)
rgp->node_states[node].pollstate = IAMALIVE_RECEIVED;
else if (rgp->node_states[node].status == RGP_NODE_COMING_UP)
{
/* If the node has not yet been marked fully up, it is time to
* do so.
*/
rgp_monitor_node(EXT_NODE(node));
/* We must tell the OS that the new node is up in case the
* OS needs the IamAlives to figure that out.
*/
rgp_newnode_online(EXT_NODE(node));
}
else
/* If the node state is neither alive nor coming up, it
* must not be in our outerscreen. The outerscreen check
* above must have passed and we should not get here.
*/
RGP_ERROR(RGP_INTERNAL_ERROR);
break;
}
case RGP_UNACK_REGROUP :
{
/* Count the number of regroup status packets received. */
RGP_INCREMENT_COUNTER( RcvdRegroup );
/* Any good packet can be treated as an IamAlive packet. */
rgp->node_states[node].pollstate = IAMALIVE_RECEIVED;
RGP_EVENT_HANDLER_EX (RGP_EVT_RECEIVED_PACKET, EXT_NODE(node), (void*)unseq_pkt);
break;
}
case RGP_UNACK_POISON :
{
/* If our node is in RGP_PRUNING stage and have been pruned out,
* the poison packet probably implies that the sender has gone
* into the next stage and declared us down. In this case, use
* the more appropriate RGP_PRUNED_OUT halt code. Otherwise,
* use the poison packet halt code. In either case, we must halt.
*/
if ( (rgp->rgppkt.stage == RGP_PRUNING) &&
!ClusterMember(rgp->rgppkt.pruning_result, rgp->mynode) )
RGP_ERROR(RGP_PRUNED_OUT);
else
{
if (rgp->rgppkt.stage == RGP_COLDLOADED)
{
RGP_ERROR(RGP_RELOADFAILED);
return;
}
else
RGP_ERROR((uint16) (RGP_PARIAH + EXT_NODE(node)));
}
break;
}
default :
{
/* Ignore the unknown packet type. */
break;
}
}
}
/*---------------------------------------------------------------------------*/
#ifdef __cplusplus
}
#endif /* __cplusplus */
#if 0
History of changes to this file:
-------------------------------------------------------------------------
1995, December 13 F40:KSK0610 /*F40:KSK06102.6*/
This file is part of the portable Regroup Module used in the NonStop
Kernel (NSK) and Loosely Coupled UNIX (LCU) operating systems. There
are 10 files in the module - jrgp.h, jrgpos.h, wrgp.h, wrgpos.h,
srgpif.c, srgpos.c, srgpsm.c, srgputl.c, srgpcli.c and srgpsvr.c.
The last two are simulation files to test the Regroup Module on a
UNIX workstation in user mode with processes simulating processor nodes
and UDP datagrams used to send unacknowledged datagrams.
This file was first submitted for release into NSK on 12/13/95.
------------------------------------------------------------------------------
This change occurred on 19 Jan 1996 /*F40:MB06458.1*/
Changes for phase IV Sierra message system release. Includes: /*F40:MB06458.2*/
- Some cleanup of the code /*F40:MB06458.3*/
- Increment KCCB counters to count the number of setup messages and /*F40:MB06458.4*/
unsequenced messages sent. /*F40:MB06458.5*/
- Fixed some bugs /*F40:MB06458.6*/
- Disable interrupts before allocating broadcast sibs. /*F40:MB06458.7*/
- Change per-packet-timeout to 5ms /*F40:MB06458.8*/
- Make the regroup and powerfail broadcast use highest priority /*F40:MB06458.9*/
tnet services queue. /*F40:MB06458.10*/
- Call the millicode backdoor to get the processor status from SP /*F40:MB06458.11*/
- Fixed expand bug in msg_listen_ and msg_readctrl_ /*F40:MB06458.12*/
- Added enhancement to msngr_sendmsg_ so that clients do not need /*F40:MB06458.13*/
to be unstoppable before calling this routine. /*F40:MB06458.14*/
- Added new steps in the build file called /*F40:MB06458.15*/
MSGSYS_C - compiles all the message system C files /*F40:MB06458.16*/
MSDRIVER - compiles all the MSDriver files /*F40:MB06458.17*/
REGROUP - compiles all the regroup files /*F40:MB06458.18*/
- remove #pragma env libspace because we set it as a command line /*F40:MB06458.19*/
parameter. /*F40:MB06458.20*/
----------------------------------------------------------------------- /*F40:MB06458.21*/
#endif /* 0 - change descriptions */