root/usr/src/uts/common/io/ib/ibtl/ibtl_cm.c
/*
 * 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);
}