/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include <sys/ib/ibtl/impl/ibtl.h>
#include <sys/ib/ibtl/impl/ibtl_cm.h>

/*
 * ibtl_cm.c
 *    These routines tie the Communication Manager into IBTL.
 */

/*
 * Globals.
 */
static char 		ibtf_cm[] = "ibtl_cm";
boolean_t		ibtl_fast_gid_cache_valid = B_FALSE;

/*
 * Function:
 *	ibtl_cm_set_chan_private
 * Input:
 *	chan		Channel Handle.
 *	cm_private	CM private data.
 * Output:
 *	none.
 * Returns:
 *	none.
 * Description:
 *	A helper function to store CM's Private data in the specified channel.
 */
void
ibtl_cm_set_chan_private(ibt_channel_hdl_t chan, void *cm_private)
{
	IBTF_DPRINTF_L3(ibtf_cm, "ibtl_cm_set_chan_private(%p, %p)",
	    chan, cm_private);

	mutex_enter(&chan->ch_cm_mutex);
	chan->ch_cm_private = cm_private;
	if (cm_private == NULL)
		cv_signal(&chan->ch_cm_cv);
	mutex_exit(&chan->ch_cm_mutex);
}


/*
 * Function:
 *	ibtl_cm_get_chan_private
 * Input:
 *	chan		Channel Handle.
 * Output:
 *	cm_private_p	The CM private data.
 * Returns:
 *	CM private data.
 * Description:
 *	A helper function to get CM's Private data for the specified channel.
 */
void *
ibtl_cm_get_chan_private(ibt_channel_hdl_t chan)
{
	void *cm_private;

	IBTF_DPRINTF_L3(ibtf_cm, "ibtl_cm_get_chan_private(%p)", chan);
	mutex_enter(&chan->ch_cm_mutex);
	cm_private = chan->ch_cm_private;
#ifndef __lock_lint
	/* IBCM will call the release function if cm_private is non-NULL */
	if (cm_private == NULL)
#endif
		mutex_exit(&chan->ch_cm_mutex);
	return (cm_private);
}

void
ibtl_cm_release_chan_private(ibt_channel_hdl_t chan)
{
#ifndef __lock_lint
	mutex_exit(&chan->ch_cm_mutex);
#endif
}

void
ibtl_cm_wait_chan_private(ibt_channel_hdl_t chan)
{
	mutex_enter(&chan->ch_cm_mutex);
	if (chan->ch_cm_private != NULL)
		cv_wait(&chan->ch_cm_cv, &chan->ch_cm_mutex);
	mutex_exit(&chan->ch_cm_mutex);
	delay(drv_usectohz(50000));
}


/*
 * Function:
 *	ibtl_cm_get_chan_type
 * Input:
 *	chan		Channel Handle.
 * Output:
 *	none.
 * Returns:
 *	Channel transport type.
 * Description:
 *	A helper function to get channel transport type.
 */
ibt_tran_srv_t
ibtl_cm_get_chan_type(ibt_channel_hdl_t chan)
{
	IBTF_DPRINTF_L3(ibtf_cm, "ibtl_cm_get_chan_type(%p)", chan);

	return (chan->ch_qp.qp_type);
}

/*
 * Function:
 *	ibtl_cm_change_service_cnt
 * Input:
 *	ibt_hdl		Client's IBT Handle.
 *	delta_num_sids	The change in the number of service ids
 *			(positive for ibt_register_service() and
 *			negative fo ibt_service_deregister()).
 */
void
ibtl_cm_change_service_cnt(ibt_clnt_hdl_t ibt_hdl, int delta_num_sids)
{
	IBTF_DPRINTF_L3(ibtf_cm, "ibtl_cm_change_service_cnt(%p. %d)",
	    ibt_hdl, delta_num_sids);

	mutex_enter(&ibtl_clnt_list_mutex);
	if ((delta_num_sids < 0) && (-delta_num_sids > ibt_hdl->clnt_srv_cnt)) {
		IBTF_DPRINTF_L2(ibtf_cm, "ibtl_cm_change_service_cnt: "
		    "ERROR: service registration counter underflow\n"
		    "current count = %d, requested delta = %d",
		    ibt_hdl->clnt_srv_cnt, delta_num_sids);
	}
	ibt_hdl->clnt_srv_cnt += delta_num_sids;
	mutex_exit(&ibtl_clnt_list_mutex);
}


/*
 * Function:
 *	ibtl_cm_get_hca_port
 * Input:
 *	gid		Source GID.
 *	hca_guid	Optional source HCA GUID on which SGID is available.
 *			Ignored if zero.
 * Output:
 *	hca_port	Pointer to ibtl_cm_hca_port_t struct.
 * Returns:
 *	IBT_SUCCESS.
 * Description:
 *	A helper function to get HCA node GUID, Base LID, SGID Index,
 *	port number, LMC and MTU for the specified SGID.
 *	Also filling default SGID, to be used in ibmf_sa_session_open.
 */
ibt_status_t
ibtl_cm_get_hca_port(ib_gid_t gid, ib_guid_t hca_guid,
    ibtl_cm_hca_port_t *hca_port)
{
	ibtl_hca_devinfo_t	*hca_devp;	/* HCA Dev Info */
	ibt_hca_portinfo_t	*portinfop;
	uint_t			ports, port;
	uint_t			i;
	ib_gid_t		*sgid;
	static ib_gid_t		fast_gid;	/* fast_gid_cache data */
	static uint8_t		fast_sgid_ix;
	static ibt_hca_portinfo_t *fast_portinfop;
	static ib_guid_t	fast_node_guid;
	static ib_guid_t	fast_port_guid;

	IBTF_DPRINTF_L3(ibtf_cm, "ibtl_cm_get_hca_port(%llX:%llX, %llX)",
	    gid.gid_prefix, gid.gid_guid, hca_guid);

	if ((gid.gid_prefix == 0) || (gid.gid_guid == 0)) {
		IBTF_DPRINTF_L2(ibtf_cm, "ibtl_cm_get_hca_port: "
		    "NULL SGID specified.");
		return (IBT_INVALID_PARAM);
	}

	mutex_enter(&ibtl_clnt_list_mutex);

	if ((ibtl_fast_gid_cache_valid == B_TRUE) &&
	    (gid.gid_guid == fast_gid.gid_guid) &&
	    (gid.gid_prefix == fast_gid.gid_prefix)) {

		if ((hca_guid != 0) && (hca_guid != fast_node_guid)) {
			IBTF_DPRINTF_L3(ibtf_cm, "ibtl_cm_get_hca_port: "
			    "Mis-match hca_guid v/s sgid combination.");
			mutex_exit(&ibtl_clnt_list_mutex);
			return (IBT_INVALID_PARAM);
		}

		portinfop = fast_portinfop;
		hca_port->hp_base_lid = portinfop->p_base_lid;
		hca_port->hp_port = portinfop->p_port_num;
		hca_port->hp_sgid_ix = fast_sgid_ix;
		hca_port->hp_lmc = portinfop->p_lmc;
		hca_port->hp_mtu = portinfop->p_mtu;
		hca_port->hp_hca_guid = fast_node_guid;
		hca_port->hp_port_guid = fast_port_guid;

		mutex_exit(&ibtl_clnt_list_mutex);

		return (IBT_SUCCESS);
	}

	/* If HCA GUID is specified, then lookup in that device only. */
	if (hca_guid) {
		hca_devp = ibtl_get_hcadevinfo(hca_guid);
	} else {
		hca_devp = ibtl_hca_list;
	}

	while (hca_devp != NULL) {

		ports = hca_devp->hd_hca_attr->hca_nports;
		portinfop = hca_devp->hd_portinfop;

		for (port = 0; port < ports; port++, portinfop++) {
			if (portinfop->p_linkstate != IBT_PORT_ACTIVE)
				continue;
			sgid = &portinfop->p_sgid_tbl[0];
			for (i = 0; i < portinfop->p_sgid_tbl_sz; i++, sgid++) {
				if ((gid.gid_guid != sgid->gid_guid) ||
				    (gid.gid_prefix != sgid->gid_prefix))
					continue;

				/*
				 * Found the matching GID.
				 */
				ibtl_fast_gid_cache_valid = B_TRUE;
				fast_gid = gid;
				fast_portinfop = portinfop;
				fast_node_guid = hca_port->hp_hca_guid =
				    hca_devp->hd_hca_attr->hca_node_guid;
				fast_sgid_ix = hca_port->hp_sgid_ix = i;
				fast_port_guid =
				    portinfop->p_sgid_tbl[0].gid_guid;
				hca_port->hp_port_guid = fast_port_guid;
				hca_port->hp_base_lid = portinfop->p_base_lid;
				hca_port->hp_port = portinfop->p_port_num;
				hca_port->hp_lmc = portinfop->p_lmc;
				hca_port->hp_mtu = portinfop->p_mtu;

				mutex_exit(&ibtl_clnt_list_mutex);

				return (IBT_SUCCESS);
			}
		}

		/* Asked to look in the specified HCA device only?. */
		if (hca_guid)
			break;

		/* Get next in the list */
		hca_devp = hca_devp->hd_hca_dev_link;
	}

	mutex_exit(&ibtl_clnt_list_mutex);

	/* If we are here, then we failed to get a match, so return error. */
	return (IBT_INVALID_PARAM);
}


static ibt_status_t
ibtl_cm_get_cnt(ibt_path_attr_t *attr, ibt_path_flags_t flags,
    ibtl_cm_port_list_t *plistp, uint_t *count)
{
	ibtl_hca_devinfo_t	*hdevp;
	ibt_hca_portinfo_t	*pinfop;
	ib_guid_t		hca_guid, tmp_hca_guid = 0;
	ib_gid_t		gid;
	uint_t			pcount = 0, tmp_pcount = 0;
	uint_t			cnt = *count;
	ibt_status_t		retval = IBT_SUCCESS;
	uint_t			i, j;

	*count = 0;

	/* If HCA GUID is specified, then lookup in that device only. */
	if (attr->pa_hca_guid) {
		hdevp = ibtl_get_hcadevinfo(attr->pa_hca_guid);
	} else {
		hdevp = ibtl_hca_list;
	}

	while (hdevp != NULL) {
		hca_guid = hdevp->hd_hca_attr->hca_node_guid;

		if ((flags & IBT_PATH_APM) &&
		    (!(hdevp->hd_hca_attr->hca_flags &
		    IBT_HCA_AUTO_PATH_MIG))) {

			IBTF_DPRINTF_L2(ibtf_cm, "ibtl_cm_get_cnt: "
			    "HCA (%llX) - APM NOT SUPPORTED ", hca_guid);

			retval = IBT_APM_NOT_SUPPORTED;

			if (attr->pa_hca_guid)
				break;
			goto search_next;
		}

		for (i = 0; i < hdevp->hd_hca_attr->hca_nports; i++) {

			if ((attr->pa_hca_port_num) &&
			    (attr->pa_hca_port_num != (i + 1))) {
				IBTF_DPRINTF_L3(ibtf_cm, "ibtl_cm_get_cnt: "
				    "Asked only on Port# %d, so skip this "
				    "port(%d)", attr->pa_hca_port_num, (i + 1));
				continue;
			}
			pinfop = hdevp->hd_portinfop + i;

			if (pinfop->p_linkstate != IBT_PORT_ACTIVE) {
				retval = IBT_HCA_PORT_NOT_ACTIVE;
				continue;
			}
			if (attr->pa_mtu.r_mtu) {
				if ((attr->pa_mtu.r_selector == IBT_GT) &&
				    (attr->pa_mtu.r_mtu >= pinfop->p_mtu))
					continue;
				else if ((attr->pa_mtu.r_selector == IBT_EQU) &&
				    (attr->pa_mtu.r_mtu > pinfop->p_mtu))
					continue;
			}

			if ((flags & IBT_PATH_APM) && (!attr->pa_hca_guid) &&
			    attr->pa_sgid.gid_prefix &&
			    attr->pa_sgid.gid_guid) {
				for (j = 0; j < pinfop->p_sgid_tbl_sz; j++) {
					gid = pinfop->p_sgid_tbl[j];
					if (gid.gid_prefix && gid.gid_guid) {
						if ((attr->pa_sgid.gid_prefix !=
						    gid.gid_prefix) ||
						    (attr->pa_sgid.gid_guid !=
						    gid.gid_guid)) {
							continue;
						} else {
							attr->pa_hca_guid =
							    hca_guid;
							goto got_apm_hca_info;
						}
					}
				}
				continue;
			}
got_apm_hca_info:
			for (j = 0; j < pinfop->p_sgid_tbl_sz; j++) {
				gid = pinfop->p_sgid_tbl[j];
				if (gid.gid_prefix && gid.gid_guid) {
					if (!(flags & IBT_PATH_APM) &&
					    attr->pa_sgid.gid_prefix &&
					    attr->pa_sgid.gid_guid) {
						if ((attr->pa_sgid.gid_prefix !=
						    gid.gid_prefix) ||
						    (attr->pa_sgid.gid_guid !=
						    gid.gid_guid))
							continue;
					}
					pcount++;
					if (plistp) {
						plistp->p_hca_guid = hca_guid;
						plistp->p_mtu = pinfop->p_mtu;
						plistp->p_base_lid =
						    pinfop->p_base_lid;
						plistp->p_port_num =
						    pinfop->p_port_num;
						plistp->p_sgid_ix = j;
						plistp->p_sgid = gid;
						plistp->p_count = cnt;
						if (hdevp->hd_multism)
							plistp->p_multi |=
							    IBTL_CM_MULTI_SM;

						IBTF_DPRINTF_L3(ibtf_cm,
						    "ibtl_cm_get_cnt: HCA"
						    "(%llX,%d) SGID(%llX:%llX)",
						    plistp->p_hca_guid,
						    plistp->p_port_num,
						    plistp->p_sgid.gid_prefix,
						    plistp->p_sgid.gid_guid);

						plistp++;
					}
				}
			}
		}
		/* Asked to look in the specified HCA device only?. */
		if (attr->pa_hca_guid)
			break;

		if (flags & IBT_PATH_APM) {
			if (pcount == 2) {
				attr->pa_hca_guid = hca_guid;
				break;
			} else if (pcount == 1) {
				if (hdevp->hd_hca_dev_link) {
					tmp_hca_guid = hca_guid;
					tmp_pcount = pcount;
					pcount = 0;
				} else if (tmp_hca_guid) {
					attr->pa_hca_guid = tmp_hca_guid;
				} else {
					attr->pa_hca_guid = hca_guid;
				}
			} else if ((pcount == 0) && (tmp_hca_guid)) {
				attr->pa_hca_guid = tmp_hca_guid;
				pcount = tmp_pcount;
			}
		}
search_next:
		hdevp = hdevp->hd_hca_dev_link;
	}

	*count = pcount;

	if (pcount) {
		retval = IBT_SUCCESS;
	} else {
		IBTF_DPRINTF_L2(ibtf_cm, "ibtl_cm_get_cnt: "
		    "Appropriate Source Points NOT found");
		if (retval == IBT_SUCCESS)
			retval = IBT_NO_HCAS_AVAILABLE;
	}

	return (retval);
}


ibt_status_t
ibtl_cm_get_active_plist(ibt_path_attr_t *attr, ibt_path_flags_t flags,
    ibtl_cm_port_list_t **port_list_p)
{
	ibtl_cm_port_list_t	*p_listp, tmp;
	uint_t			i, j;
	uint_t			count, rcount;
	boolean_t		multi_hca = B_FALSE;
	ibt_status_t		retval = IBT_SUCCESS;

	IBTF_DPRINTF_L3(ibtf_cm, "ibtl_cm_get_active_plist(%p, %X)",
	    attr, flags);

get_plist_start:
	*port_list_p = NULL;

	/* Get "number of active src points" so that we can allocate memory. */
	mutex_enter(&ibtl_clnt_list_mutex);
	retval = ibtl_cm_get_cnt(attr, flags, NULL, &count);
	mutex_exit(&ibtl_clnt_list_mutex);

	IBTF_DPRINTF_L3(ibtf_cm, "ibtl_cm_get_active_plist: Found %d SrcPoint",
	    count);
	if (retval != IBT_SUCCESS)
		return (retval);

	/* Allocate Memory to hold Src Point information. */
	p_listp = kmem_zalloc(count * sizeof (ibtl_cm_port_list_t), KM_SLEEP);

	/*
	 * Verify that the count we got previously is still valid, as we had
	 * dropped mutex to allocate memory. If not, restart the process.
	 */
	mutex_enter(&ibtl_clnt_list_mutex);
	retval = ibtl_cm_get_cnt(attr, flags, NULL, &rcount);
	if (retval != IBT_SUCCESS) {
		mutex_exit(&ibtl_clnt_list_mutex);
		kmem_free(p_listp, count * sizeof (ibtl_cm_port_list_t));
		return (retval);
	} else if (rcount != count) {
		mutex_exit(&ibtl_clnt_list_mutex);
		kmem_free(p_listp, count * sizeof (ibtl_cm_port_list_t));
		goto get_plist_start;
	}

	*port_list_p = p_listp;
	/*
	 * Src count hasn't changed, still holding the lock fill-in the
	 * required source point information.
	 */
	retval = ibtl_cm_get_cnt(attr, flags, p_listp, &rcount);
	mutex_exit(&ibtl_clnt_list_mutex);
	if (retval != IBT_SUCCESS) {
		kmem_free(p_listp, count * sizeof (ibtl_cm_port_list_t));
		*port_list_p = NULL;
		return (retval);
	}

	p_listp = *port_list_p;

	_NOTE(NO_COMPETING_THREADS_NOW)

	for (i = 0; i < count - 1; i++) {
		for (j = 0; j < count - 1 - i; j++) {
			if (p_listp[j].p_hca_guid != p_listp[j+1].p_hca_guid) {
				multi_hca = B_TRUE;
				break;
			}
		}
		if (multi_hca == B_TRUE)
			break;
	}

	if (multi_hca == B_TRUE)
		for (i = 0; i < count; i++)
			p_listp[i].p_multi |= IBTL_CM_MULTI_HCA;

	/*
	 * Sort (bubble sort) the list based on MTU quality (higher on top).
	 * Sorting is only performed, if IBT_PATH_AVAIL is set.
	 */
	if (((attr->pa_mtu.r_selector == IBT_GT) || (flags & IBT_PATH_AVAIL)) &&
	    (!(flags & IBT_PATH_APM))) {
		for (i = 0; i < count - 1; i++) {
			for (j = 0; j < count - 1 - i; j++) {
				if (p_listp[j].p_mtu < p_listp[j+1].p_mtu) {
					tmp = p_listp[j];
					p_listp[j] = p_listp[j+1];
					p_listp[j+1] = tmp;
				}
			}
		}
	}

	if ((p_listp->p_multi & IBTL_CM_MULTI_HCA) &&
	    (flags & IBT_PATH_AVAIL) && (!(flags & IBT_PATH_APM))) {
		/* Avoid having same HCA next to each other in the list. */
		for (i = 0; i < count - 1; i++) {
			for (j = 0; j < (count - 1 - i); j++) {
				if ((p_listp[j].p_hca_guid ==
				    p_listp[j+1].p_hca_guid) &&
				    (j+2 < count)) {
					tmp = p_listp[j+1];
					p_listp[j+1] = p_listp[j+2];
					p_listp[j+2] = tmp;
				}
			}
		}
	}

	/*
	 * If SGID is specified, then make sure that SGID info is first
	 * in the array.
	 */
	if (attr->pa_sgid.gid_guid && (p_listp->p_count > 1) &&
	    (p_listp[0].p_sgid.gid_guid != attr->pa_sgid.gid_guid)) {
		for (i = 1; i < count; i++) {
			if (p_listp[i].p_sgid.gid_guid ==
			    attr->pa_sgid.gid_guid) {
				tmp = p_listp[i];
				p_listp[i] = p_listp[0];
				p_listp[0] = tmp;
			}
		}
	}

#ifndef lint
	_NOTE(COMPETING_THREADS_NOW)
#endif

	IBTF_DPRINTF_L3(ibtf_cm, "ibtl_cm_get_active_plist: "
	    "Returned <%d> entries @0x%p", count, *port_list_p);

	return (retval);
}


void
ibtl_cm_free_active_plist(ibtl_cm_port_list_t *plist)
{
	int count;

	IBTF_DPRINTF_L3(ibtf_cm, "ibtl_cm_free_active_plist(%p)", plist);

	if (plist != NULL) {
		_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*plist))
		count = plist->p_count;
		_NOTE(NOW_VISIBLE_TO_OTHER_THREADS(*plist))

		kmem_free(plist, count * sizeof (ibtl_cm_port_list_t));
	}
}

/*
 * Function:
 *	ibtl_cm_get_1st_full_pkey_ix
 * Input:
 *	hca_guid	HCA GUID.
 *	port		Port Number.
 * Output:
 *	None.
 * Returns:
 *	P_Key Index of the first full member available from the P_Key table
 *	of the specified HCA<->Port.
 * Description:
 *	A helper function to get P_Key Index of the first full member P_Key
 *	available on the specified HCA and Port combination.
 */
uint16_t
ibtl_cm_get_1st_full_pkey_ix(ib_guid_t hca_guid, uint8_t port)
{
	ibtl_hca_devinfo_t	*hca_devp;	/* HCA Dev Info */
	uint16_t		pkey_ix = 0;

	IBTF_DPRINTF_L3(ibtf_cm, "ibtl_cm_get_1st_full_pkey_ix(%llX, %d)",
	    hca_guid, port);

	mutex_enter(&ibtl_clnt_list_mutex);
	hca_devp = ibtl_get_hcadevinfo(hca_guid);

	if ((hca_devp != NULL) && (port <= hca_devp->hd_hca_attr->hca_nports) &&
	    (port != 0)) {
		pkey_ix = hca_devp->hd_portinfop[port - 1].p_def_pkey_ix;
	} else {
		IBTF_DPRINTF_L2(ibtf_cm, "ibtl_cm_get_1st_full_pkey_ix: "
		    "Invalid HCA (%llX), Port (%d) specified.", hca_guid, port);
	}
	mutex_exit(&ibtl_clnt_list_mutex);

	return (pkey_ix);
}


ibt_status_t
ibtl_cm_get_local_comp_gids(ib_guid_t hca_guid, ib_gid_t gid, ib_gid_t **gids_p,
    uint_t *num_gids_p)
{
	ibtl_hca_devinfo_t	*hdevp;	/* HCA Dev Info */
	ibt_hca_portinfo_t	*pinfop;
	ib_gid_t		sgid;
	ib_gid_t		*gidp = NULL;
	int			i, j, k;
	int			count = 0;
	int			gid_specified;

	IBTF_DPRINTF_L3(ibtf_cm, "ibtl_cm_get_local_comp_gids(%llX, %llX:%llX)",
	    hca_guid, gid.gid_prefix, gid.gid_guid);

	mutex_enter(&ibtl_clnt_list_mutex);
	hdevp = ibtl_get_hcadevinfo(hca_guid);

	if (hdevp == NULL) {
		IBTF_DPRINTF_L3(ibtf_cm, "ibtl_cm_get_local_comp_gids: ",
		    "NO HCA (%llX) availble", hca_guid);
		mutex_exit(&ibtl_clnt_list_mutex);
		return (IBT_NO_HCAS_AVAILABLE);
	}

	if (gid.gid_prefix && gid.gid_guid)
		gid_specified = 1;
	else
		gid_specified = 0;

	for (i = 0; i < hdevp->hd_hca_attr->hca_nports; i++) {
		pinfop = hdevp->hd_portinfop + i;

		if (pinfop->p_linkstate != IBT_PORT_ACTIVE)
			continue;

		for (j = 0; j < pinfop->p_sgid_tbl_sz; j++) {
			sgid = pinfop->p_sgid_tbl[j];
			if (sgid.gid_prefix && sgid.gid_guid) {
				if (gid_specified &&
				    ((gid.gid_prefix == sgid.gid_prefix) &&
				    (gid.gid_guid == sgid.gid_guid))) {
					/*
					 * Don't return the input specified
					 * GID
					 */
					continue;
				}
				count++;
			}
		}
	}

	if (count == 0) {
		IBTF_DPRINTF_L2(ibtf_cm, "ibtl_cm_get_local_comp_gids: "
		    "Companion GIDs not available");
		mutex_exit(&ibtl_clnt_list_mutex);
		return (IBT_GIDS_NOT_FOUND);
	}

	gidp = kmem_zalloc(count * sizeof (ib_gid_t), KM_SLEEP);
	*num_gids_p = count;
	*gids_p = gidp;
	k = 0;

	for (i = 0; i < hdevp->hd_hca_attr->hca_nports; i++) {
		pinfop = hdevp->hd_portinfop + i;

		if (pinfop->p_linkstate != IBT_PORT_ACTIVE)
			continue;

		for (j = 0; j < pinfop->p_sgid_tbl_sz; j++) {
			sgid = pinfop->p_sgid_tbl[j];
			if (sgid.gid_prefix && sgid.gid_guid) {
				if (gid_specified &&
				    ((gid.gid_prefix == sgid.gid_prefix) &&
				    (gid.gid_guid == sgid.gid_guid)))
					continue;

				gidp[k].gid_prefix = sgid.gid_prefix;
				gidp[k].gid_guid = sgid.gid_guid;

				IBTF_DPRINTF_L3(ibtf_cm,
				    "ibtl_cm_get_local_comp_gids: GID[%d]="
				    "%llX:%llX", k, gidp[k].gid_prefix,
				    gidp[k].gid_guid);
				k++;
				if (k == count)
					break;
			}
		}
		if (k == count)
			break;
	}
	mutex_exit(&ibtl_clnt_list_mutex);

	return (IBT_SUCCESS);
}


int
ibtl_cm_is_multi_sm(ib_guid_t hca_guid)
{
	ibtl_hca_devinfo_t	*hdevp;	/* HCA Dev Info */
	uint_t			multi_sm;

	mutex_enter(&ibtl_clnt_list_mutex);
	hdevp = ibtl_get_hcadevinfo(hca_guid);
	if (hdevp == NULL) {
		IBTF_DPRINTF_L2(ibtf_cm, "ibtl_cm_is_multi_sm: NO HCA (%llX) "
		    "availble", hca_guid);
		mutex_exit(&ibtl_clnt_list_mutex);
		return (-1);
	}
	multi_sm = hdevp->hd_multism;
	mutex_exit(&ibtl_clnt_list_mutex);

	IBTF_DPRINTF_L3(ibtf_cm, "ibtl_cm_is_multi_sm(%llX): %d", hca_guid,
	    multi_sm);

	return (multi_sm);
}

char *
ibtl_cm_get_clnt_name(ibt_clnt_hdl_t ibt_hdl)
{
	if (ibt_hdl)
		return (ibt_hdl->clnt_name);
	else
		return (NULL);
}