Blame opensm/osm_switch.c

Packit Service 54dbc3
/*
Packit Service 54dbc3
 * Copyright (c) 2004-2009 Voltaire, Inc. All rights reserved.
Packit Service 54dbc3
 * Copyright (c) 2002-2015 Mellanox Technologies LTD. All rights reserved.
Packit Service 54dbc3
 * Copyright (c) 1996-2003 Intel Corporation. All rights reserved.
Packit Service 54dbc3
 * Copyright (c) 2009 HNR Consulting. All rights reserved.
Packit Service 54dbc3
 *
Packit Service 54dbc3
 * This software is available to you under a choice of one of two
Packit Service 54dbc3
 * licenses.  You may choose to be licensed under the terms of the GNU
Packit Service 54dbc3
 * General Public License (GPL) Version 2, available from the file
Packit Service 54dbc3
 * COPYING in the main directory of this source tree, or the
Packit Service 54dbc3
 * OpenIB.org BSD license below:
Packit Service 54dbc3
 *
Packit Service 54dbc3
 *     Redistribution and use in source and binary forms, with or
Packit Service 54dbc3
 *     without modification, are permitted provided that the following
Packit Service 54dbc3
 *     conditions are met:
Packit Service 54dbc3
 *
Packit Service 54dbc3
 *      - Redistributions of source code must retain the above
Packit Service 54dbc3
 *        copyright notice, this list of conditions and the following
Packit Service 54dbc3
 *        disclaimer.
Packit Service 54dbc3
 *
Packit Service 54dbc3
 *      - Redistributions in binary form must reproduce the above
Packit Service 54dbc3
 *        copyright notice, this list of conditions and the following
Packit Service 54dbc3
 *        disclaimer in the documentation and/or other materials
Packit Service 54dbc3
 *        provided with the distribution.
Packit Service 54dbc3
 *
Packit Service 54dbc3
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
Packit Service 54dbc3
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
Packit Service 54dbc3
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
Packit Service 54dbc3
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
Packit Service 54dbc3
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
Packit Service 54dbc3
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
Packit Service 54dbc3
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
Packit Service 54dbc3
 * SOFTWARE.
Packit Service 54dbc3
 *
Packit Service 54dbc3
 */
Packit Service 54dbc3
Packit Service 54dbc3
/*
Packit Service 54dbc3
 * Abstract:
Packit Service 54dbc3
 *    Implementation of osm_switch_t.
Packit Service 54dbc3
 * This object represents an Infiniband switch.
Packit Service 54dbc3
 * This object is part of the opensm family of objects.
Packit Service 54dbc3
 */
Packit Service 54dbc3
Packit Service 54dbc3
#if HAVE_CONFIG_H
Packit Service 54dbc3
#  include <config.h>
Packit Service 54dbc3
#endif				/* HAVE_CONFIG_H */
Packit Service 54dbc3
Packit Service 54dbc3
#include <stdlib.h>
Packit Service 54dbc3
#include <string.h>
Packit Service 54dbc3
#include <complib/cl_math.h>
Packit Service 54dbc3
#include <iba/ib_types.h>
Packit Service 54dbc3
#include <opensm/osm_file_ids.h>
Packit Service 54dbc3
#define FILE_ID OSM_FILE_SWITCH_C
Packit Service 54dbc3
#include <opensm/osm_switch.h>
Packit Service 54dbc3
Packit Service 54dbc3
struct switch_port_path {
Packit Service 54dbc3
	uint8_t port_num;
Packit Service 54dbc3
	uint32_t path_count;
Packit Service 54dbc3
	int found_sys_guid;
Packit Service 54dbc3
	int found_node_guid;
Packit Service 54dbc3
	uint32_t forwarded_to;
Packit Service 54dbc3
};
Packit Service 54dbc3
Packit Service 54dbc3
cl_status_t osm_switch_set_hops(IN osm_switch_t * p_sw, IN uint16_t lid_ho,
Packit Service 54dbc3
				IN uint8_t port_num, IN uint8_t num_hops)
Packit Service 54dbc3
{
Packit Service 54dbc3
	if (!lid_ho || lid_ho > p_sw->max_lid_ho)
Packit Service 54dbc3
		return -1;
Packit Service 54dbc3
	if (port_num >= p_sw->num_ports)
Packit Service 54dbc3
		return -1;
Packit Service 54dbc3
	if (!p_sw->hops[lid_ho]) {
Packit Service 54dbc3
		p_sw->hops[lid_ho] = malloc(p_sw->num_ports);
Packit Service 54dbc3
		if (!p_sw->hops[lid_ho])
Packit Service 54dbc3
			return -1;
Packit Service 54dbc3
		memset(p_sw->hops[lid_ho], OSM_NO_PATH, p_sw->num_ports);
Packit Service 54dbc3
	}
Packit Service 54dbc3
Packit Service 54dbc3
	p_sw->hops[lid_ho][port_num] = num_hops;
Packit Service 54dbc3
	if (p_sw->hops[lid_ho][0] > num_hops)
Packit Service 54dbc3
		p_sw->hops[lid_ho][0] = num_hops;
Packit Service 54dbc3
Packit Service 54dbc3
	return 0;
Packit Service 54dbc3
}
Packit Service 54dbc3
Packit Service 54dbc3
void osm_switch_delete(IN OUT osm_switch_t ** pp_sw)
Packit Service 54dbc3
{
Packit Service 54dbc3
	osm_switch_t *p_sw = *pp_sw;
Packit Service 54dbc3
	unsigned i;
Packit Service 54dbc3
Packit Service 54dbc3
	osm_mcast_tbl_destroy(&p_sw->mcast_tbl);
Packit Service 54dbc3
	if (p_sw->p_prof)
Packit Service 54dbc3
		free(p_sw->p_prof);
Packit Service 54dbc3
	if (p_sw->search_ordering_ports)
Packit Service 54dbc3
		free(p_sw->search_ordering_ports);
Packit Service 54dbc3
	if (p_sw->lft)
Packit Service 54dbc3
		free(p_sw->lft);
Packit Service 54dbc3
	if (p_sw->new_lft)
Packit Service 54dbc3
		free(p_sw->new_lft);
Packit Service 54dbc3
	if (p_sw->hops) {
Packit Service 54dbc3
		for (i = 0; i < p_sw->num_hops; i++)
Packit Service 54dbc3
			if (p_sw->hops[i])
Packit Service 54dbc3
				free(p_sw->hops[i]);
Packit Service 54dbc3
		free(p_sw->hops);
Packit Service 54dbc3
	}
Packit Service 54dbc3
	free(*pp_sw);
Packit Service 54dbc3
	*pp_sw = NULL;
Packit Service 54dbc3
}
Packit Service 54dbc3
Packit Service 54dbc3
osm_switch_t *osm_switch_new(IN osm_node_t * p_node,
Packit Service 54dbc3
			     IN const osm_madw_t * p_madw)
Packit Service 54dbc3
{
Packit Service 54dbc3
	osm_switch_t *p_sw;
Packit Service 54dbc3
	ib_switch_info_t *p_si;
Packit Service 54dbc3
	ib_smp_t *p_smp;
Packit Service 54dbc3
	uint8_t num_ports;
Packit Service 54dbc3
	uint32_t port_num;
Packit Service 54dbc3
Packit Service 54dbc3
	CL_ASSERT(p_madw);
Packit Service 54dbc3
	CL_ASSERT(p_node);
Packit Service 54dbc3
Packit Service 54dbc3
	p_smp = osm_madw_get_smp_ptr(p_madw);
Packit Service 54dbc3
	p_si = ib_smp_get_payload_ptr(p_smp);
Packit Service 54dbc3
	num_ports = osm_node_get_num_physp(p_node);
Packit Service 54dbc3
Packit Service 54dbc3
	CL_ASSERT(p_smp->attr_id == IB_MAD_ATTR_SWITCH_INFO);
Packit Service 54dbc3
Packit Service 54dbc3
	if (!p_si->lin_cap) /* The switch doesn't support LFT */
Packit Service 54dbc3
		return NULL;
Packit Service 54dbc3
Packit Service 54dbc3
	p_sw = malloc(sizeof(*p_sw));
Packit Service 54dbc3
	if (!p_sw)
Packit Service 54dbc3
		return NULL;
Packit Service 54dbc3
Packit Service 54dbc3
	memset(p_sw, 0, sizeof(*p_sw));
Packit Service 54dbc3
Packit Service 54dbc3
	p_sw->p_node = p_node;
Packit Service 54dbc3
	p_sw->switch_info = *p_si;
Packit Service 54dbc3
	p_sw->num_ports = num_ports;
Packit Service 54dbc3
	p_sw->need_update = 2;
Packit Service 54dbc3
Packit Service 54dbc3
	p_sw->p_prof = malloc(sizeof(*p_sw->p_prof) * num_ports);
Packit Service 54dbc3
	if (!p_sw->p_prof)
Packit Service 54dbc3
		goto err;
Packit Service 54dbc3
Packit Service 54dbc3
	memset(p_sw->p_prof, 0, sizeof(*p_sw->p_prof) * num_ports);
Packit Service 54dbc3
Packit Service 54dbc3
	osm_mcast_tbl_init(&p_sw->mcast_tbl, osm_node_get_num_physp(p_node),
Packit Service 54dbc3
			   cl_ntoh16(p_si->mcast_cap));
Packit Service 54dbc3
Packit Service 54dbc3
	for (port_num = 0; port_num < num_ports; port_num++)
Packit Service 54dbc3
		osm_port_prof_construct(&p_sw->p_prof[port_num]);
Packit Service 54dbc3
Packit Service 54dbc3
	return p_sw;
Packit Service 54dbc3
Packit Service 54dbc3
err:
Packit Service 54dbc3
	osm_switch_delete(&p_sw);
Packit Service 54dbc3
	return NULL;
Packit Service 54dbc3
}
Packit Service 54dbc3
Packit Service 54dbc3
boolean_t osm_switch_get_lft_block(IN const osm_switch_t * p_sw,
Packit Service 54dbc3
				   IN uint16_t block_id, OUT uint8_t * p_block)
Packit Service 54dbc3
{
Packit Service 54dbc3
	uint16_t base_lid_ho = block_id * IB_SMP_DATA_SIZE;
Packit Service 54dbc3
Packit Service 54dbc3
	CL_ASSERT(p_sw);
Packit Service 54dbc3
	CL_ASSERT(p_block);
Packit Service 54dbc3
Packit Service 54dbc3
	if (base_lid_ho > p_sw->max_lid_ho)
Packit Service 54dbc3
		return FALSE;
Packit Service 54dbc3
Packit Service 54dbc3
	CL_ASSERT(base_lid_ho + IB_SMP_DATA_SIZE - 1 <= IB_LID_UCAST_END_HO);
Packit Service 54dbc3
	memcpy(p_block, &(p_sw->new_lft[base_lid_ho]), IB_SMP_DATA_SIZE);
Packit Service 54dbc3
	return TRUE;
Packit Service 54dbc3
}
Packit Service 54dbc3
Packit Service 54dbc3
static struct osm_remote_node *
Packit Service 54dbc3
switch_find_guid_common(IN const osm_switch_t * p_sw,
Packit Service 54dbc3
			IN struct osm_remote_guids_count *r,
Packit Service 54dbc3
			IN uint8_t port_num, IN int find_sys_guid,
Packit Service 54dbc3
			IN int find_node_guid)
Packit Service 54dbc3
{
Packit Service 54dbc3
	struct osm_remote_node *p_remote_guid = NULL;
Packit Service 54dbc3
	osm_physp_t *p_physp;
Packit Service 54dbc3
	osm_physp_t *p_rem_physp;
Packit Service 54dbc3
	osm_node_t *p_rem_node;
Packit Service 54dbc3
	uint64_t sys_guid;
Packit Service 54dbc3
	uint64_t node_guid;
Packit Service 54dbc3
	unsigned int i;
Packit Service 54dbc3
Packit Service 54dbc3
	CL_ASSERT(p_sw);
Packit Service 54dbc3
Packit Service 54dbc3
	if (!r)
Packit Service 54dbc3
		goto out;
Packit Service 54dbc3
Packit Service 54dbc3
	p_physp = osm_node_get_physp_ptr(p_sw->p_node, port_num);
Packit Service 54dbc3
	if (!p_physp)
Packit Service 54dbc3
		goto out;
Packit Service 54dbc3
Packit Service 54dbc3
	p_rem_physp = osm_physp_get_remote(p_physp);
Packit Service 54dbc3
	p_rem_node = osm_physp_get_node_ptr(p_rem_physp);
Packit Service 54dbc3
	sys_guid = p_rem_node->node_info.sys_guid;
Packit Service 54dbc3
	node_guid = p_rem_node->node_info.node_guid;
Packit Service 54dbc3
Packit Service 54dbc3
	for (i = 0; i < r->count; i++) {
Packit Service 54dbc3
		if ((!find_sys_guid
Packit Service 54dbc3
		     || r->guids[i].node->node_info.sys_guid == sys_guid)
Packit Service 54dbc3
		    && (!find_node_guid
Packit Service 54dbc3
			|| r->guids[i].node->node_info.node_guid == node_guid)) {
Packit Service 54dbc3
			p_remote_guid = &r->guids[i];
Packit Service 54dbc3
			break;
Packit Service 54dbc3
		}
Packit Service 54dbc3
	}
Packit Service 54dbc3
Packit Service 54dbc3
out:
Packit Service 54dbc3
	return p_remote_guid;
Packit Service 54dbc3
}
Packit Service 54dbc3
Packit Service 54dbc3
static struct osm_remote_node *
Packit Service 54dbc3
switch_find_sys_guid_count(IN const osm_switch_t * p_sw,
Packit Service 54dbc3
			   IN struct osm_remote_guids_count *r,
Packit Service 54dbc3
			   IN uint8_t port_num)
Packit Service 54dbc3
{
Packit Service 54dbc3
	return switch_find_guid_common(p_sw, r, port_num, 1, 0);
Packit Service 54dbc3
}
Packit Service 54dbc3
Packit Service 54dbc3
static struct osm_remote_node *
Packit Service 54dbc3
switch_find_node_guid_count(IN const osm_switch_t * p_sw,
Packit Service 54dbc3
			    IN struct osm_remote_guids_count *r,
Packit Service 54dbc3
			    IN uint8_t port_num)
Packit Service 54dbc3
{
Packit Service 54dbc3
	return switch_find_guid_common(p_sw, r, port_num, 0, 1);
Packit Service 54dbc3
}
Packit Service 54dbc3
Packit Service 54dbc3
uint8_t osm_switch_recommend_path(IN const osm_switch_t * p_sw,
Packit Service 54dbc3
				  IN osm_port_t * p_port, IN uint16_t lid_ho,
Packit Service 54dbc3
				  IN unsigned start_from,
Packit Service 54dbc3
				  IN boolean_t ignore_existing,
Packit Service 54dbc3
				  IN boolean_t routing_for_lmc,
Packit Service 54dbc3
				  IN boolean_t dor,
Packit Service 54dbc3
				  IN boolean_t port_shifting,
Packit Service 54dbc3
				  IN uint32_t scatter_ports,
Packit Service 54dbc3
				  IN osm_lft_type_enum lft_enum)
Packit Service 54dbc3
{
Packit Service 54dbc3
	/*
Packit Service 54dbc3
	   We support an enhanced LMC aware routing mode:
Packit Service 54dbc3
	   In the case of LMC > 0, we can track the remote side
Packit Service 54dbc3
	   system and node for all of the lids of the target
Packit Service 54dbc3
	   and try and avoid routing again through the same
Packit Service 54dbc3
	   system / node.
Packit Service 54dbc3
Packit Service 54dbc3
	   Assume if routing_for_lmc is true that this procedure was
Packit Service 54dbc3
	   provided the tracking array and counter via p_port->priv,
Packit Service 54dbc3
	   and we can conduct this algorithm.
Packit Service 54dbc3
	 */
Packit Service 54dbc3
	uint16_t base_lid;
Packit Service 54dbc3
	uint8_t hops;
Packit Service 54dbc3
	uint8_t least_hops;
Packit Service 54dbc3
	uint8_t port_num;
Packit Service 54dbc3
	uint8_t num_ports;
Packit Service 54dbc3
	uint32_t least_paths = 0xFFFFFFFF;
Packit Service 54dbc3
	unsigned i;
Packit Service 54dbc3
	/*
Packit Service 54dbc3
	   The following will track the least paths if the
Packit Service 54dbc3
	   route should go through a new system/node
Packit Service 54dbc3
	 */
Packit Service 54dbc3
	uint32_t least_paths_other_sys = 0xFFFFFFFF;
Packit Service 54dbc3
	uint32_t least_paths_other_nodes = 0xFFFFFFFF;
Packit Service 54dbc3
	uint32_t least_forwarded_to = 0xFFFFFFFF;
Packit Service 54dbc3
	uint32_t check_count;
Packit Service 54dbc3
	uint8_t best_port = 0;
Packit Service 54dbc3
	/*
Packit Service 54dbc3
	   These vars track the best port if it connects to
Packit Service 54dbc3
	   not used system/node.
Packit Service 54dbc3
	 */
Packit Service 54dbc3
	uint8_t best_port_other_sys = 0;
Packit Service 54dbc3
	uint8_t best_port_other_node = 0;
Packit Service 54dbc3
	boolean_t port_found = FALSE;
Packit Service 54dbc3
	osm_physp_t *p_physp;
Packit Service 54dbc3
	osm_physp_t *p_rem_physp;
Packit Service 54dbc3
	osm_node_t *p_rem_node;
Packit Service 54dbc3
	osm_node_t *p_rem_node_first = NULL;
Packit Service 54dbc3
	struct osm_remote_node *p_remote_guid = NULL;
Packit Service 54dbc3
	struct osm_remote_node null_remote_node = {NULL, 0, 0};
Packit Service 54dbc3
	struct switch_port_path port_paths[IB_NODE_NUM_PORTS_MAX];
Packit Service 54dbc3
	unsigned int port_paths_total_paths = 0;
Packit Service 54dbc3
	unsigned int port_paths_count = 0;
Packit Service 54dbc3
	uint8_t scatter_possible_ports[IB_NODE_NUM_PORTS_MAX];
Packit Service 54dbc3
	unsigned int scatter_possible_ports_count = 0;
Packit Service 54dbc3
	int found_sys_guid = 0;
Packit Service 54dbc3
	int found_node_guid = 0;
Packit Service 54dbc3
Packit Service 54dbc3
	CL_ASSERT(lid_ho > 0);
Packit Service 54dbc3
Packit Service 54dbc3
	if (p_port->p_node->sw) {
Packit Service 54dbc3
		if (p_port->p_node->sw == p_sw)
Packit Service 54dbc3
			return 0;
Packit Service 54dbc3
		base_lid = osm_port_get_base_lid(p_port);
Packit Service 54dbc3
	} else {
Packit Service 54dbc3
		p_physp = p_port->p_physp;
Packit Service 54dbc3
		if (!p_physp || !p_physp->p_remote_physp ||
Packit Service 54dbc3
		    !p_physp->p_remote_physp->p_node->sw)
Packit Service 54dbc3
			return OSM_NO_PATH;
Packit Service 54dbc3
Packit Service 54dbc3
		if (p_physp->p_remote_physp->p_node->sw == p_sw)
Packit Service 54dbc3
			return p_physp->p_remote_physp->port_num;
Packit Service 54dbc3
		base_lid =
Packit Service 54dbc3
		    osm_node_get_base_lid(p_physp->p_remote_physp->p_node, 0);
Packit Service 54dbc3
	}
Packit Service 54dbc3
	base_lid = cl_ntoh16(base_lid);
Packit Service 54dbc3
Packit Service 54dbc3
	num_ports = p_sw->num_ports;
Packit Service 54dbc3
Packit Service 54dbc3
	least_hops = osm_switch_get_least_hops(p_sw, base_lid);
Packit Service 54dbc3
	if (least_hops == OSM_NO_PATH)
Packit Service 54dbc3
		return OSM_NO_PATH;
Packit Service 54dbc3
Packit Service 54dbc3
	/*
Packit Service 54dbc3
	   First, inquire with the forwarding table for an existing
Packit Service 54dbc3
	   route.  If one is found, honor it unless:
Packit Service 54dbc3
	   1. the ignore existing flag is set.
Packit Service 54dbc3
	   2. the physical port is not a valid one or not healthy
Packit Service 54dbc3
	   3. the physical port has a remote port (the link is up)
Packit Service 54dbc3
	   4. the port has min-hops to the target (avoid loops)
Packit Service 54dbc3
	 */
Packit Service 54dbc3
	if (!ignore_existing) {
Packit Service 54dbc3
		port_num = osm_switch_get_port_by_lid(p_sw, lid_ho, lft_enum);
Packit Service 54dbc3
Packit Service 54dbc3
		if (port_num != OSM_NO_PATH) {
Packit Service 54dbc3
			CL_ASSERT(port_num < num_ports);
Packit Service 54dbc3
Packit Service 54dbc3
			p_physp =
Packit Service 54dbc3
			    osm_node_get_physp_ptr(p_sw->p_node, port_num);
Packit Service 54dbc3
			/*
Packit Service 54dbc3
			   Don't be too trusting of the current forwarding table!
Packit Service 54dbc3
			   Verify that the port number is legal and that the
Packit Service 54dbc3
			   LID is reachable through this port.
Packit Service 54dbc3
			 */
Packit Service 54dbc3
			if (p_physp && osm_physp_is_healthy(p_physp) &&
Packit Service 54dbc3
			    osm_physp_get_remote(p_physp)) {
Packit Service 54dbc3
				hops =
Packit Service 54dbc3
				    osm_switch_get_hop_count(p_sw, base_lid,
Packit Service 54dbc3
							     port_num);
Packit Service 54dbc3
				/*
Packit Service 54dbc3
				   If we aren't using pre-defined user routes
Packit Service 54dbc3
				   function, then we need to make sure that the
Packit Service 54dbc3
				   current path is the minimum one. In case of
Packit Service 54dbc3
				   having such a user function - this check will
Packit Service 54dbc3
				   not be done, and the old routing will be used.
Packit Service 54dbc3
				   Note: This means that it is the user's job to
Packit Service 54dbc3
				   clean all data in the forwarding tables that
Packit Service 54dbc3
				   he wants to be overridden by the minimum
Packit Service 54dbc3
				   hop function.
Packit Service 54dbc3
				 */
Packit Service 54dbc3
				if (hops == least_hops)
Packit Service 54dbc3
					return port_num;
Packit Service 54dbc3
			}
Packit Service 54dbc3
		}
Packit Service 54dbc3
	}
Packit Service 54dbc3
Packit Service 54dbc3
	/*
Packit Service 54dbc3
	   This algorithm selects a port based on a static load balanced
Packit Service 54dbc3
	   selection across equal hop-count ports.
Packit Service 54dbc3
	   There is lots of room for improved sophistication here,
Packit Service 54dbc3
	   possibly guided by user configuration info.
Packit Service 54dbc3
	 */
Packit Service 54dbc3
Packit Service 54dbc3
	/*
Packit Service 54dbc3
	   OpenSM routing is "local" - not considering a full lid to lid
Packit Service 54dbc3
	   path. As such we can not guarantee a path will not loop if we
Packit Service 54dbc3
	   do not always follow least hops.
Packit Service 54dbc3
	   So we must abort if not least hops.
Packit Service 54dbc3
	 */
Packit Service 54dbc3
Packit Service 54dbc3
	/* port number starts with one and num_ports is 1 + num phys ports */
Packit Service 54dbc3
	for (i = start_from; i < start_from + num_ports; i++) {
Packit Service 54dbc3
		port_num = osm_switch_get_dimn_port(p_sw, i % num_ports);
Packit Service 54dbc3
		if (!port_num ||
Packit Service 54dbc3
		    osm_switch_get_hop_count(p_sw, base_lid, port_num) !=
Packit Service 54dbc3
		    least_hops)
Packit Service 54dbc3
			continue;
Packit Service 54dbc3
Packit Service 54dbc3
		/* let us make sure it is not down or unhealthy */
Packit Service 54dbc3
		p_physp = osm_node_get_physp_ptr(p_sw->p_node, port_num);
Packit Service 54dbc3
		if (!p_physp || !osm_physp_is_healthy(p_physp) ||
Packit Service 54dbc3
		    /*
Packit Service 54dbc3
		       we require all - non sma ports to be linked
Packit Service 54dbc3
		       to be routed through
Packit Service 54dbc3
		     */
Packit Service 54dbc3
		    !osm_physp_get_remote(p_physp))
Packit Service 54dbc3
			continue;
Packit Service 54dbc3
Packit Service 54dbc3
		/*
Packit Service 54dbc3
		   We located a least-hop port, possibly one of many.
Packit Service 54dbc3
		   For this port, check the running total count of
Packit Service 54dbc3
		   the number of paths through this port.  Select
Packit Service 54dbc3
		   the port routing the least number of paths.
Packit Service 54dbc3
		 */
Packit Service 54dbc3
		check_count =
Packit Service 54dbc3
		    osm_port_prof_path_count_get(&p_sw->p_prof[port_num]);
Packit Service 54dbc3
Packit Service 54dbc3
Packit Service 54dbc3
		if (dor) {
Packit Service 54dbc3
			/* Get the Remote Node */
Packit Service 54dbc3
			p_rem_physp = osm_physp_get_remote(p_physp);
Packit Service 54dbc3
			p_rem_node = osm_physp_get_node_ptr(p_rem_physp);
Packit Service 54dbc3
			/* use the first dimension, but spread traffic
Packit Service 54dbc3
			 * out among the group of ports representing
Packit Service 54dbc3
			 * that dimension */
Packit Service 54dbc3
			if (!p_rem_node_first)
Packit Service 54dbc3
				p_rem_node_first = p_rem_node;
Packit Service 54dbc3
			else if (p_rem_node != p_rem_node_first)
Packit Service 54dbc3
				continue;
Packit Service 54dbc3
			if (routing_for_lmc) {
Packit Service 54dbc3
				struct osm_remote_guids_count *r = p_port->priv;
Packit Service 54dbc3
				uint8_t rem_port = osm_physp_get_port_num(p_rem_physp);
Packit Service 54dbc3
				unsigned int j;
Packit Service 54dbc3
Packit Service 54dbc3
				for (j = 0; j < r->count; j++) {
Packit Service 54dbc3
					p_remote_guid = &r->guids[j];
Packit Service 54dbc3
					if ((p_remote_guid->node == p_rem_node)
Packit Service 54dbc3
					    && (p_remote_guid->port == rem_port))
Packit Service 54dbc3
						break;
Packit Service 54dbc3
				}
Packit Service 54dbc3
				if (j == r->count)
Packit Service 54dbc3
					p_remote_guid = &null_remote_node;
Packit Service 54dbc3
			}
Packit Service 54dbc3
		/*
Packit Service 54dbc3
		   Advanced LMC routing requires tracking of the
Packit Service 54dbc3
		   best port by the node connected to the other side of
Packit Service 54dbc3
		   it.
Packit Service 54dbc3
		 */
Packit Service 54dbc3
		} else if (routing_for_lmc) {
Packit Service 54dbc3
			/* Is the sys guid already used ? */
Packit Service 54dbc3
			p_remote_guid = switch_find_sys_guid_count(p_sw,
Packit Service 54dbc3
								   p_port->priv,
Packit Service 54dbc3
								   port_num);
Packit Service 54dbc3
Packit Service 54dbc3
			/* If not update the least hops for this case */
Packit Service 54dbc3
			if (!p_remote_guid) {
Packit Service 54dbc3
				if (check_count < least_paths_other_sys) {
Packit Service 54dbc3
					least_paths_other_sys = check_count;
Packit Service 54dbc3
					best_port_other_sys = port_num;
Packit Service 54dbc3
					least_forwarded_to = 0;
Packit Service 54dbc3
				}
Packit Service 54dbc3
				found_sys_guid = 0;
Packit Service 54dbc3
			} else {	/* same sys found - try node */
Packit Service 54dbc3
Packit Service 54dbc3
Packit Service 54dbc3
				/* Else is the node guid already used ? */
Packit Service 54dbc3
				p_remote_guid = switch_find_node_guid_count(p_sw,
Packit Service 54dbc3
									    p_port->priv,
Packit Service 54dbc3
									    port_num);
Packit Service 54dbc3
Packit Service 54dbc3
				/* If not update the least hops for this case */
Packit Service 54dbc3
				if (!p_remote_guid
Packit Service 54dbc3
				    && check_count < least_paths_other_nodes) {
Packit Service 54dbc3
					least_paths_other_nodes = check_count;
Packit Service 54dbc3
					best_port_other_node = port_num;
Packit Service 54dbc3
					least_forwarded_to = 0;
Packit Service 54dbc3
				}
Packit Service 54dbc3
				/* else prior sys and node guid already used */
Packit Service 54dbc3
Packit Service 54dbc3
				if (!p_remote_guid)
Packit Service 54dbc3
					found_node_guid = 0;
Packit Service 54dbc3
				else
Packit Service 54dbc3
					found_node_guid = 1;
Packit Service 54dbc3
				found_sys_guid = 1;
Packit Service 54dbc3
			}	/* same sys found */
Packit Service 54dbc3
		}
Packit Service 54dbc3
Packit Service 54dbc3
		port_paths[port_paths_count].port_num = port_num;
Packit Service 54dbc3
		port_paths[port_paths_count].path_count = check_count;
Packit Service 54dbc3
		if (routing_for_lmc) {
Packit Service 54dbc3
			port_paths[port_paths_count].found_sys_guid = found_sys_guid;
Packit Service 54dbc3
			port_paths[port_paths_count].found_node_guid = found_node_guid;
Packit Service 54dbc3
		}
Packit Service 54dbc3
		if (routing_for_lmc && p_remote_guid)
Packit Service 54dbc3
			port_paths[port_paths_count].forwarded_to = p_remote_guid->forwarded_to;
Packit Service 54dbc3
		else
Packit Service 54dbc3
			port_paths[port_paths_count].forwarded_to = 0;
Packit Service 54dbc3
		port_paths_total_paths += check_count;
Packit Service 54dbc3
		port_paths_count++;
Packit Service 54dbc3
Packit Service 54dbc3
		/* routing for LMC mode */
Packit Service 54dbc3
		/*
Packit Service 54dbc3
		   the count is min but also lower then the max subscribed
Packit Service 54dbc3
		 */
Packit Service 54dbc3
		if (check_count < least_paths) {
Packit Service 54dbc3
			port_found = TRUE;
Packit Service 54dbc3
			best_port = port_num;
Packit Service 54dbc3
			least_paths = check_count;
Packit Service 54dbc3
			scatter_possible_ports_count = 0;
Packit Service 54dbc3
			scatter_possible_ports[scatter_possible_ports_count++] = port_num;
Packit Service 54dbc3
			if (routing_for_lmc
Packit Service 54dbc3
			    && p_remote_guid
Packit Service 54dbc3
			    && p_remote_guid->forwarded_to < least_forwarded_to)
Packit Service 54dbc3
				least_forwarded_to = p_remote_guid->forwarded_to;
Packit Service 54dbc3
		} else if (scatter_ports
Packit Service 54dbc3
			   && check_count == least_paths) {
Packit Service 54dbc3
			scatter_possible_ports[scatter_possible_ports_count++] = port_num;
Packit Service 54dbc3
		} else if (routing_for_lmc
Packit Service 54dbc3
			   && p_remote_guid
Packit Service 54dbc3
			   && check_count == least_paths
Packit Service 54dbc3
			   && p_remote_guid->forwarded_to < least_forwarded_to) {
Packit Service 54dbc3
			least_forwarded_to = p_remote_guid->forwarded_to;
Packit Service 54dbc3
			best_port = port_num;
Packit Service 54dbc3
		}
Packit Service 54dbc3
	}
Packit Service 54dbc3
Packit Service 54dbc3
	if (port_found == FALSE)
Packit Service 54dbc3
		return OSM_NO_PATH;
Packit Service 54dbc3
Packit Service 54dbc3
	if (port_shifting && port_paths_count) {
Packit Service 54dbc3
		/* In the port_paths[] array, we now have all the ports that we
Packit Service 54dbc3
		 * can route out of.  Using some shifting math below, possibly
Packit Service 54dbc3
		 * select a different one so that lids won't align in LFTs
Packit Service 54dbc3
		 *
Packit Service 54dbc3
		 * If lmc > 0, we need to loop through these ports to find the
Packit Service 54dbc3
		 * least_forwarded_to port, best_port_other_sys, and
Packit Service 54dbc3
		 * best_port_other_node just like before but through the different
Packit Service 54dbc3
		 * ordering.
Packit Service 54dbc3
		 */
Packit Service 54dbc3
Packit Service 54dbc3
		least_paths = 0xFFFFFFFF;
Packit Service 54dbc3
		least_paths_other_sys = 0xFFFFFFFF;
Packit Service 54dbc3
		least_paths_other_nodes = 0xFFFFFFFF;
Packit Service 54dbc3
	        least_forwarded_to = 0xFFFFFFFF;
Packit Service 54dbc3
		best_port = 0;
Packit Service 54dbc3
		best_port_other_sys = 0;
Packit Service 54dbc3
		best_port_other_node = 0;
Packit Service 54dbc3
Packit Service 54dbc3
		for (i = 0; i < port_paths_count; i++) {
Packit Service 54dbc3
			unsigned int idx;
Packit Service 54dbc3
Packit Service 54dbc3
			idx = (port_paths_total_paths/port_paths_count + i) % port_paths_count;
Packit Service 54dbc3
Packit Service 54dbc3
			if (routing_for_lmc) {
Packit Service 54dbc3
				if (!port_paths[idx].found_sys_guid
Packit Service 54dbc3
				    && port_paths[idx].path_count < least_paths_other_sys) {
Packit Service 54dbc3
					least_paths_other_sys = port_paths[idx].path_count;
Packit Service 54dbc3
					best_port_other_sys = port_paths[idx].port_num;
Packit Service 54dbc3
					least_forwarded_to = 0;
Packit Service 54dbc3
				}
Packit Service 54dbc3
				else if (!port_paths[idx].found_node_guid
Packit Service 54dbc3
					 && port_paths[idx].path_count < least_paths_other_nodes) {
Packit Service 54dbc3
					least_paths_other_nodes = port_paths[idx].path_count;
Packit Service 54dbc3
					best_port_other_node = port_paths[idx].port_num;
Packit Service 54dbc3
					least_forwarded_to = 0;
Packit Service 54dbc3
				}
Packit Service 54dbc3
			}
Packit Service 54dbc3
Packit Service 54dbc3
			if (port_paths[idx].path_count < least_paths) {
Packit Service 54dbc3
				best_port = port_paths[idx].port_num;
Packit Service 54dbc3
				least_paths = port_paths[idx].path_count;
Packit Service 54dbc3
				if (routing_for_lmc
Packit Service 54dbc3
				    && (port_paths[idx].found_sys_guid
Packit Service 54dbc3
					|| port_paths[idx].found_node_guid)
Packit Service 54dbc3
				    && port_paths[idx].forwarded_to < least_forwarded_to)
Packit Service 54dbc3
					least_forwarded_to = port_paths[idx].forwarded_to;
Packit Service 54dbc3
			}
Packit Service 54dbc3
			else if (routing_for_lmc
Packit Service 54dbc3
				 && (port_paths[idx].found_sys_guid
Packit Service 54dbc3
				     || port_paths[idx].found_node_guid)
Packit Service 54dbc3
				 && port_paths[idx].path_count == least_paths
Packit Service 54dbc3
				 && port_paths[idx].forwarded_to < least_forwarded_to) {
Packit Service 54dbc3
				least_forwarded_to = port_paths[idx].forwarded_to;
Packit Service 54dbc3
				best_port = port_paths[idx].port_num;
Packit Service 54dbc3
			}
Packit Service 54dbc3
Packit Service 54dbc3
		}
Packit Service 54dbc3
	}
Packit Service 54dbc3
Packit Service 54dbc3
	/*
Packit Service 54dbc3
	   if we are in enhanced routing mode and the best port is not
Packit Service 54dbc3
	   the local port 0
Packit Service 54dbc3
	 */
Packit Service 54dbc3
	if (routing_for_lmc && best_port && !scatter_ports) {
Packit Service 54dbc3
		/* Select the least hop port of the non used sys first */
Packit Service 54dbc3
		if (best_port_other_sys)
Packit Service 54dbc3
			best_port = best_port_other_sys;
Packit Service 54dbc3
		else if (best_port_other_node)
Packit Service 54dbc3
			best_port = best_port_other_node;
Packit Service 54dbc3
	} else if (scatter_ports) {
Packit Service 54dbc3
		/*
Packit Service 54dbc3
		 * There is some danger that this random could "rebalance" the routes
Packit Service 54dbc3
		 * every time, to combat this there is a global srandom that
Packit Service 54dbc3
		 * occurs at the start of every sweep.
Packit Service 54dbc3
		 */
Packit Service 54dbc3
		unsigned int idx = random() % scatter_possible_ports_count;
Packit Service 54dbc3
		best_port = scatter_possible_ports[idx];
Packit Service 54dbc3
	}
Packit Service 54dbc3
	return best_port;
Packit Service 54dbc3
}
Packit Service 54dbc3
Packit Service 54dbc3
void osm_switch_clear_hops(IN osm_switch_t * p_sw)
Packit Service 54dbc3
{
Packit Service 54dbc3
	unsigned i;
Packit Service 54dbc3
Packit Service 54dbc3
	for (i = 0; i < p_sw->num_hops; i++)
Packit Service 54dbc3
		if (p_sw->hops[i])
Packit Service 54dbc3
			memset(p_sw->hops[i], OSM_NO_PATH, p_sw->num_ports);
Packit Service 54dbc3
}
Packit Service 54dbc3
Packit Service 54dbc3
static int alloc_lft(IN osm_switch_t * p_sw, uint16_t lids)
Packit Service 54dbc3
{
Packit Service 54dbc3
	uint16_t lft_size;
Packit Service 54dbc3
Packit Service 54dbc3
	/* Ensure LFT is in units of LFT block size */
Packit Service 54dbc3
	lft_size = (lids / IB_SMP_DATA_SIZE + 1) * IB_SMP_DATA_SIZE;
Packit Service 54dbc3
	if (lft_size > p_sw->lft_size) {
Packit Service 54dbc3
		uint8_t *new_lft = realloc(p_sw->lft, lft_size);
Packit Service 54dbc3
		if (!new_lft)
Packit Service 54dbc3
			return -1;
Packit Service 54dbc3
		memset(new_lft + p_sw->lft_size, OSM_NO_PATH,
Packit Service 54dbc3
		       lft_size - p_sw->lft_size);
Packit Service 54dbc3
		p_sw->lft = new_lft;
Packit Service 54dbc3
		p_sw->lft_size = lft_size;
Packit Service 54dbc3
	}
Packit Service 54dbc3
Packit Service 54dbc3
	return 0;
Packit Service 54dbc3
}
Packit Service 54dbc3
Packit Service 54dbc3
int osm_switch_prepare_path_rebuild(IN osm_switch_t * p_sw, IN uint16_t max_lids)
Packit Service 54dbc3
{
Packit Service 54dbc3
	uint8_t **hops;
Packit Service 54dbc3
	uint8_t *new_lft;
Packit Service 54dbc3
	unsigned i;
Packit Service 54dbc3
Packit Service 54dbc3
	if (alloc_lft(p_sw, max_lids))
Packit Service 54dbc3
		return -1;
Packit Service 54dbc3
Packit Service 54dbc3
	for (i = 0; i < p_sw->num_ports; i++)
Packit Service 54dbc3
		osm_port_prof_construct(&p_sw->p_prof[i]);
Packit Service 54dbc3
Packit Service 54dbc3
	osm_switch_clear_hops(p_sw);
Packit Service 54dbc3
Packit Service 54dbc3
	if (!(new_lft = realloc(p_sw->new_lft, p_sw->lft_size)))
Packit Service 54dbc3
		return -1;
Packit Service 54dbc3
Packit Service 54dbc3
	p_sw->new_lft = new_lft;
Packit Service 54dbc3
Packit Service 54dbc3
	memset(p_sw->new_lft, OSM_NO_PATH, p_sw->lft_size);
Packit Service 54dbc3
Packit Service 54dbc3
	if (!p_sw->hops) {
Packit Service 54dbc3
		hops = malloc((max_lids + 1) * sizeof(hops[0]));
Packit Service 54dbc3
		if (!hops)
Packit Service 54dbc3
			return -1;
Packit Service 54dbc3
		memset(hops, 0, (max_lids + 1) * sizeof(hops[0]));
Packit Service 54dbc3
		p_sw->hops = hops;
Packit Service 54dbc3
		p_sw->num_hops = max_lids + 1;
Packit Service 54dbc3
	} else if (max_lids + 1 > p_sw->num_hops) {
Packit Service 54dbc3
		hops = realloc(p_sw->hops, (max_lids + 1) * sizeof(hops[0]));
Packit Service 54dbc3
		if (!hops)
Packit Service 54dbc3
			return -1;
Packit Service 54dbc3
		memset(hops + p_sw->num_hops, 0,
Packit Service 54dbc3
		       (max_lids + 1 - p_sw->num_hops) * sizeof(hops[0]));
Packit Service 54dbc3
		p_sw->hops = hops;
Packit Service 54dbc3
		p_sw->num_hops = max_lids + 1;
Packit Service 54dbc3
	}
Packit Service 54dbc3
	p_sw->max_lid_ho = max_lids;
Packit Service 54dbc3
Packit Service 54dbc3
	return 0;
Packit Service 54dbc3
}
Packit Service 54dbc3
Packit Service 54dbc3
uint8_t osm_switch_get_port_least_hops(IN const osm_switch_t * p_sw,
Packit Service 54dbc3
				       IN const osm_port_t * p_port)
Packit Service 54dbc3
{
Packit Service 54dbc3
	uint16_t lid;
Packit Service 54dbc3
Packit Service 54dbc3
	if (p_port->p_node->sw) {
Packit Service 54dbc3
		if (p_port->p_node->sw == p_sw)
Packit Service 54dbc3
			return 0;
Packit Service 54dbc3
		lid = osm_node_get_base_lid(p_port->p_node, 0);
Packit Service 54dbc3
		return osm_switch_get_least_hops(p_sw, cl_ntoh16(lid));
Packit Service 54dbc3
	} else {
Packit Service 54dbc3
		osm_physp_t *p = p_port->p_physp;
Packit Service 54dbc3
		uint8_t hops;
Packit Service 54dbc3
Packit Service 54dbc3
		if (!p || !p->p_remote_physp || !p->p_remote_physp->p_node->sw)
Packit Service 54dbc3
			return OSM_NO_PATH;
Packit Service 54dbc3
		if (p->p_remote_physp->p_node->sw == p_sw)
Packit Service 54dbc3
			return 1;
Packit Service 54dbc3
		lid = osm_node_get_base_lid(p->p_remote_physp->p_node, 0);
Packit Service 54dbc3
		hops = osm_switch_get_least_hops(p_sw, cl_ntoh16(lid));
Packit Service 54dbc3
		return hops != OSM_NO_PATH ? hops + 1 : OSM_NO_PATH;
Packit Service 54dbc3
	}
Packit Service 54dbc3
}
Packit Service 54dbc3
Packit Service 54dbc3
uint8_t osm_switch_recommend_mcast_path(IN osm_switch_t * p_sw,
Packit Service 54dbc3
					IN osm_port_t * p_port,
Packit Service 54dbc3
					IN uint16_t mlid_ho,
Packit Service 54dbc3
					IN boolean_t ignore_existing)
Packit Service 54dbc3
{
Packit Service 54dbc3
	uint16_t base_lid;
Packit Service 54dbc3
	uint8_t hops;
Packit Service 54dbc3
	uint8_t port_num;
Packit Service 54dbc3
	uint8_t num_ports;
Packit Service 54dbc3
	uint8_t least_hops;
Packit Service 54dbc3
Packit Service 54dbc3
	CL_ASSERT(mlid_ho >= IB_LID_MCAST_START_HO);
Packit Service 54dbc3
Packit Service 54dbc3
	if (p_port->p_node->sw) {
Packit Service 54dbc3
		if (p_port->p_node->sw == p_sw)
Packit Service 54dbc3
			return 0;
Packit Service 54dbc3
		base_lid = osm_port_get_base_lid(p_port);
Packit Service 54dbc3
	} else {
Packit Service 54dbc3
		osm_physp_t *p_physp = p_port->p_physp;
Packit Service 54dbc3
		if (!p_physp || !p_physp->p_remote_physp ||
Packit Service 54dbc3
		    !p_physp->p_remote_physp->p_node->sw)
Packit Service 54dbc3
			return OSM_NO_PATH;
Packit Service 54dbc3
		if (p_physp->p_remote_physp->p_node->sw == p_sw)
Packit Service 54dbc3
			return p_physp->p_remote_physp->port_num;
Packit Service 54dbc3
		base_lid =
Packit Service 54dbc3
		    osm_node_get_base_lid(p_physp->p_remote_physp->p_node, 0);
Packit Service 54dbc3
	}
Packit Service 54dbc3
	base_lid = cl_ntoh16(base_lid);
Packit Service 54dbc3
	num_ports = p_sw->num_ports;
Packit Service 54dbc3
Packit Service 54dbc3
	/*
Packit Service 54dbc3
	   If the user wants us to ignore existing multicast routes,
Packit Service 54dbc3
	   then simply return the shortest hop count path to the
Packit Service 54dbc3
	   target port.
Packit Service 54dbc3
Packit Service 54dbc3
	   Otherwise, return the first port that has a path to the target,
Packit Service 54dbc3
	   picking from the ports that are already in the multicast group.
Packit Service 54dbc3
	 */
Packit Service 54dbc3
	if (!ignore_existing) {
Packit Service 54dbc3
		for (port_num = 1; port_num < num_ports; port_num++) {
Packit Service 54dbc3
			if (!osm_mcast_tbl_is_port
Packit Service 54dbc3
			    (&p_sw->mcast_tbl, mlid_ho, port_num))
Packit Service 54dbc3
				continue;
Packit Service 54dbc3
			/*
Packit Service 54dbc3
			   Don't be too trusting of the current forwarding table!
Packit Service 54dbc3
			   Verify that the LID is reachable through this port.
Packit Service 54dbc3
			 */
Packit Service 54dbc3
			hops =
Packit Service 54dbc3
			    osm_switch_get_hop_count(p_sw, base_lid, port_num);
Packit Service 54dbc3
			if (hops != OSM_NO_PATH)
Packit Service 54dbc3
				return port_num;
Packit Service 54dbc3
		}
Packit Service 54dbc3
	}
Packit Service 54dbc3
Packit Service 54dbc3
	/*
Packit Service 54dbc3
	   Either no existing mcast paths reach this port or we are
Packit Service 54dbc3
	   ignoring existing paths.
Packit Service 54dbc3
Packit Service 54dbc3
	   Determine the best multicast path to the target.  Note that this
Packit Service 54dbc3
	   algorithm is slightly different from the one used for unicast route
Packit Service 54dbc3
	   recommendation.  In this case (multicast), we must NOT
Packit Service 54dbc3
	   perform any sort of load balancing.  We MUST take the FIRST
Packit Service 54dbc3
	   port found that has <= the lowest hop count path.  This prevents
Packit Service 54dbc3
	   more than one multicast path to the same remote switch which
Packit Service 54dbc3
	   prevents a multicast loop.  Multicast loops are bad since the same
Packit Service 54dbc3
	   multicast packet will go around and around, inevitably creating
Packit Service 54dbc3
	   a black hole that will destroy the Earth in a firey conflagration.
Packit Service 54dbc3
	 */
Packit Service 54dbc3
	least_hops = osm_switch_get_least_hops(p_sw, base_lid);
Packit Service 54dbc3
	if (least_hops == OSM_NO_PATH)
Packit Service 54dbc3
		return OSM_NO_PATH;
Packit Service 54dbc3
	for (port_num = 1; port_num < num_ports; port_num++)
Packit Service 54dbc3
		if (osm_switch_get_hop_count(p_sw, base_lid, port_num) ==
Packit Service 54dbc3
		    least_hops)
Packit Service 54dbc3
			break;
Packit Service 54dbc3
Packit Service 54dbc3
	CL_ASSERT(port_num < num_ports);
Packit Service 54dbc3
	return port_num;
Packit Service 54dbc3
}