#ifdef __TANDEM #pragma columns 79 #pragma page "srgputl.c - T9050 - utility 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 (srgputl.c) contains the cluster_t data type implementation * and the node pruning algorithm used by Regroup. *---------------------------------------------------------------------------*/ #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ #include /************************************************************************ * ClusterInit, * ClusterUnion, * ClusterIntersection, * ClusterDifference, * ClusterCompare, * ClusterSubsetOf, * ClusterComplement, * ClusterMember, * ClusterInsert, * ClusterDelete, * ClusterCopy, * ClusterSwap, * ClusterNumMembers * ================= * * Description: * * Functions that implement operations on the cluster_t type. * * Algorithm: * * Operates on the byte array that is the cluster_t type. * ************************************************************************/ _priv _resident void ClusterInit(cluster_t c) { int i; for (i = 0; i < BYTES_IN_CLUSTER; i++) c[i] = 0; } _priv _resident void ClusterUnion(cluster_t dst, cluster_t src1, cluster_t src2) { int i; for (i = 0; i < BYTES_IN_CLUSTER; i++) dst[i] = src1[i] | src2[i]; } _priv _resident void ClusterIntersection(cluster_t dst, cluster_t src1, cluster_t src2) { int i; for (i = 0; i < BYTES_IN_CLUSTER; i++) dst[i] = src1[i] & src2[i]; } _priv _resident void ClusterDifference(cluster_t dst, cluster_t src1, cluster_t src2) { int i; for (i = 0; i < BYTES_IN_CLUSTER; i++) dst[i] = src1[i] & (~src2[i]); } _priv _resident int ClusterCompare(cluster_t c1, cluster_t c2) { int identical, i; identical = 1; for (i = 0; i < BYTES_IN_CLUSTER; i++) { if (c1[i] != c2[i]) { identical = 0; break; } } return(identical); } _priv _resident int ClusterSubsetOf(cluster_t big, cluster_t small) /* Returns 1 if set small = set big or small is a subset of big. */ { int subset, i; subset = 1; for (i = 0; i < BYTES_IN_CLUSTER; i++) { if ( (big[i] != small[i]) && ((big[i] ^ small[i]) & small[i]) ) { subset = 0; break; } } return(subset); } _priv _resident void ClusterComplement(cluster_t dst, cluster_t src) { int i; for (i = 0; i < BYTES_IN_CLUSTER; i++) dst[i] = ~src[i]; } _priv _resident int ClusterMember(cluster_t c, node_t i) { return((BYTE(c,i) >> (BYTEL-1-BIT(i))) & 1); } _priv _resident void ClusterInsert(cluster_t c, node_t i) { BYTE(c, i) |= (1 << (BYTEL-1-BIT(i))); } _priv _resident void ClusterDelete(cluster_t c, node_t i) { BYTE(c, i) &= ~(1 << (BYTEL-1-BIT(i))); } _priv _resident void ClusterCopy(cluster_t dst, cluster_t src) { int i; for (i = 0; i < BYTES_IN_CLUSTER; i++) dst[i] = src[i]; } _priv _resident void ClusterSwap(cluster_t c1, cluster_t c2) { int i; unsigned char temp; for (i = 0; i < BYTES_IN_CLUSTER; i++) { temp = c1[i]; c1[i] = c2[i]; c2[i] = temp; } } _priv _resident int ClusterNumMembers(cluster_t c) /* Returns the number of nodes in the cluster. */ { int num_members = 0, i, j; for (i = 0; i < BYTES_IN_CLUSTER; i++) { if (c[i]) { for (j = 0; j < BYTEL; j++) if (c[i] & (1 << j)) num_members++; } } return(num_members); } /************************************************************************ * ClusterEmpty * ================= * * Description: * * Checks that a cluster has no members * * Parameters: * * cluster_t c * cluster to be examined * * Returns: * * 0 - cluster contains at least one node * 1 - cluster is empty * * Comment: * * The proper place for this function is in srgputl.c * ************************************************************************/ int ClusterEmpty(cluster_t c) { int i; for (i = 0; i < BYTES_IN_CLUSTER; i++) { if (c[i]) { return 0; } } return 1; } /************************************************************************ * rgp_select_tiebreaker * ===================== * * Description: * * Simple algorithm to select the tie-breaker. * * Parameters: * * cluster_t cluster - * cluster from which a tie-breaker is to be selected * * Returns: * * node_t - the node number of the selected tie-breaker * * Algorithm: * * The tie-breaker is defined as the lowest numbered node in the * cluster. * ************************************************************************/ _priv _resident node_t rgp_select_tiebreaker(cluster_t cluster) { node_t i; for (i = 0; (i < (node_t) rgp->num_nodes) && !ClusterMember(cluster, i); i++); /* If the cluster does not have any members, we have a problem! */ if (i >= (node_t) rgp->num_nodes) RGP_ERROR(RGP_INTERNAL_ERROR); return(i); } /*--------------------------------------------------------------------------- * Node pruning algorithm used by Regroup. *--------------------------------------------------------------------------- */ /************************************************************************ * group_exists * ============ * * Description: * * Check if a specific group already exists or is a subset of a * group that already exists. * * Parameters: * * cluster_t groups[] - * array of groups to examine * * int numgroups - * number of groups discovered so far * * cluster_t g - * specific group to check * * Returns: * * int - 1 if the specified group exists in the array; 0 therwise. * * Algorithm: * * Goes through the array and calls ClusterSubsetOf to check if the * specified group g is a subset of the the array element. * ************************************************************************/ #if !defined(NT) _priv _resident static int #endif group_exists(cluster_t groups[], int numgroups, cluster_t g) { int exists, i; exists = 0; for (i = 0; i < numgroups; i++) { if (ClusterSubsetOf(groups[i],g)) { exists = 1; break; } } return(exists); } /************************************************************************ * prune * ===== * * Description: * * Algorithm to find all fully connected groups based on # of * disconnects in the matrix. * * Parameters: * * disconnect_array disconnects - * input : array of disconnects * * int D - * input : size of disconnects array * * cluster_t live_nodes - * input : set of all live nodes * * cluster_t groups[] - * output: array of fully-connected groups * * Returns: * * int - the number of groups made; 0 if no groups or other error * * Algorithm: * * Start with one group that contains the set of live nodes. * More groups will be generated as disconnects are examined. * * Process each disconnect in the disconnects array by applying * the disconnect to the current set of fully-connected groups. * * The effect of a disconnect on a fully-conncted group depends on * whether the end points of the disconnect are in the group or not. * * If the group contains neither or only one of the endpoints of * the disconnect, the disconnect has no effect on the group. * * If both endpoints of the disconnect are in the group, then the * group is split into two groups - the original group without * endpoint 1 and the original group without endpoint 2. * New groups so generated should be discarded if they already * exist or are subsets of currently existing groups. * * After every disconnect is processed, we end up with the final * set of fully-connected groups. * ************************************************************************/ #if !defined(NT) _priv _resident static #endif int prune( disconnect_array disconnects, int D, cluster_t live_nodes, cluster_t groups[]) { int numgroups = 1, i, j; ClusterCopy(groups[0], live_nodes); for (i = 0; i < D; i ++) { for (j = 0; j < numgroups; j++) { /* Split a group that has both ends of the disconnect. */ if (ClusterMember(groups[j],disconnects[i][0]) && ClusterMember(groups[j],disconnects[i][1])) { /* Correct current group in place. * Add new group at the end of the array. */ numgroups++; ClusterCopy(groups[numgroups-1], groups[j]); ClusterDelete(groups[j], disconnects[i][0]); ClusterDelete(groups[numgroups-1], disconnects[i][1]); /* Check if the new groups already exist or are subgroups * of existing groups. */ /* First, check the group added at the end of the array. */ if (group_exists(groups, numgroups-1, groups[numgroups-1])) numgroups--; /* Next, check the modified group at j. * To simplify the checking, switch it with the last element * of the array. If the group already exists, it should be * removed. Since the group is now the last element of the * array, removal requires only decrementing the array count. */ ClusterSwap(groups[j], groups[numgroups-1]); if (group_exists(groups, numgroups-1, groups[numgroups-1])) numgroups--; j--; /* The j-th entry has been switched with the last entry; it has to be examined again */ } } } return(numgroups); } /************************************************************************ * select_group_with_designated_node * ================================= * * Description: * * Function to pick an arbitrary fully connected group that * includes a specified node. * * Parameters: * * connectivity_matrix_t c - * input : cluster's connectivity info * * node_t selected_node - * input : just find a fully-connected group that includes this node * * cluster_t *group - * output: group that includes selected_node * * Returns: * * int - returns 1 if the specified node is alive and 0 if it is not * * Algorithm: * * Start with a group that includes just the selected node. * Then, examine nodes starting with node 0 and go up till the * largest node number. If a node is alive, include it in the group * if and only if it is connected to all current members of the * group. * * When all nodes are examined, we get a fully-connected group that * includes the selected node. This is only one of potentially many * fully-connected groups and is not necessarily the largest * solution. * * This order of examining nodes gives higher priority to lower * numbered nodes. * ************************************************************************/ #if !defined(NT) _priv _resident static #endif int select_group_with_designated_node( connectivity_matrix_t c, node_t selected_node, cluster_t *group) { node_t i, j; if (!node_considered_alive(selected_node)) return(0); else { ClusterInit(*group); ClusterInsert(*group, selected_node); for (i = 0; i < (node_t) rgp->num_nodes; i++) { if ((i != selected_node) && node_considered_alive(i) && connected(i, selected_node) ) { /* Check if i is connected to all members of the group * built so far. */ for (j = 0; j < i; j++) { if (ClusterMember(*group, j) && !connected(j, i)) break; } if (j == i) /* i is connected to all current members*/ ClusterInsert(*group, i); } } return(1); } } /************************************************************************ * MatrixInit * ========== * * Description: * * Initialize the matrix c to show 0 connectivity. * * Parameters: * * connectivity_matrix_t c - matrix to be set to 0s. * * Returns: * * void - no return value * * Algorithm: * * Calls ClusterInit to initialize the clusters in the matrix. * ************************************************************************/ _priv _resident void MatrixInit(connectivity_matrix_t c) { int i; for (i = 0; i < (node_t) rgp->num_nodes; i++) { ClusterInit(c[i]); } } /************************************************************************ * MatrixSet * ========= * * Description: * * Set matrix[row,column] to 1. * * Parameters: * * connectivity_matrix_t c - matrix to be modified * * int row - row number * * int column - column number * * Returns: * * void - no return value * * Algorithm: * * Calls ClusterInsert to set the appropriate bit (column) in the * appropriate cluster (row) in the matrix. * ************************************************************************/ _priv _resident void MatrixSet(connectivity_matrix_t c, int row, int column) { ClusterInsert(c[row], (node_t) column); } /************************************************************************ * MatrixOr * ======== * * Description: * * matrix t := t OR s * * Parameters: * * connectivity_matrix_t t - target matrix * * connectivity_matrix_t s - source matrix to be ORed into target * * Returns: * * void - no return value * * Algorithm: * * Calls ClusterUnion to OR the appropriate clusters (rows) in the * matrices. * ************************************************************************/ _priv _resident void MatrixOr(connectivity_matrix_t t, connectivity_matrix_t s) { int i; for (i = 0; i < (node_t) rgp->num_nodes; i++) ClusterUnion(t[i], s[i], t[i]); } /************************************************************************ * connectivity_complete * ===================== * * Description: * * Boolean function that checks if a given connectivity matrix implies * full connectivity (all nodes can talk to all others). * * Parameters: * * connectivity_matrix_t c - connectivity matrix of the cluster * * Returns: * * int - 0 if there are disconnects in the cluster; 1 if it has full * connectivity. * * Algorithm: * * Checks to see if there is any live node in the cluster that cannot * communicate to another live node in the cluster. Node i is * considered alive if c[i,i] is set. Nodes i and j are deemed to * be able to communicate if c[i,j] and c[j,i] are both set. * ************************************************************************/ _priv _resident int connectivity_complete(connectivity_matrix_t c) { node_t i, j; for (i = 0; i < (node_t) rgp->num_nodes; i++) { if (node_considered_alive(i)) { for (j = 0; j < i; j++) { if (node_considered_alive(j) && !connected(i, j)) { /* i and j are a pair of live nodes which are not connected. Thus, there is at least one disconnect. Return 0. */ return(0); } } } } /* No disconnects found; return 1. */ return(1); } /************************************************************************ * find_all_fully_connected_groups * =============================== * * Description: * * Function to find all fully connected groups in a graph specified * by a connectivity matrix. An optional "selected_node" can be * used to simplify the search in case of too large a number of * possibilities. In that case, a fully-connected group that * includes selected_node is returned. * * Parameters: * * connectivity_matrix_t c - * input : cluster's connectivity info * * node_t selected_node - * input : if there are too many potential groups, just find one * that includes this node; if all groups can be listed, ignore this. * * cluster_t groups[] - * output: array of potential clusters * * Returns: * * int - the number of groups made; 0 if no groups or other error * * Algorithm: * * First the set of live nodes and the set of disconnects in the * cluster are evaluated. Then, if the number of live nodes and * disconnects indicates a potentially large number of * possibilities, select_group_with_designated_node() is called to * limit the search to a group including the specified node. * Otherwise, prune() is called to get the list of all possible * fully-connected groups. * ************************************************************************/ _priv _resident int find_all_fully_connected_groups( connectivity_matrix_t c, node_t selected_node, cluster_t groups[]) { disconnect_array disconnects; cluster_t live_nodes; int num_livenodes = 0, num_disconnects = 0; node_t i, j; ClusterInit(live_nodes); for (i = 0; i < (node_t) rgp->num_nodes; i++) { if (node_considered_alive(i)) { ClusterInsert(live_nodes, i); num_livenodes++; for (j = 0; j < i; j++) { if (node_considered_alive(j) && !connected(i, j)) { /* i and j are a pair of live nodes which are not connected. */ disconnects[num_disconnects][0] = i; disconnects[num_disconnects][1] = j; num_disconnects++; } } if (too_many_groups(num_livenodes, num_disconnects)) { RGP_TRACE( "RGP Too many dis", num_livenodes, /* TRACE */ num_disconnects, /* TRACE */ 0, 0 ); /* TRACE */ /* There may be too many choices to consider in reasonable * time/space. Just return a fully-connected group that * includes the selected node. */ return(select_group_with_designated_node(c,selected_node,groups)); } } } if (num_livenodes == 0) return(0); else return(prune(disconnects, num_disconnects, live_nodes, groups)); } /*---------------------------------------------------------------------------*/ #ifdef __cplusplus } #endif /* __cplusplus */ #if 0 History of changes to this file: ------------------------------------------------------------------------- 1995, December 13 F40:KSK0610 /*F40:KSK06102.2*/ 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 */