root/usr/src/uts/common/io/ib/mgt/ibmf/ibmf_impl.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 (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
 */

/*
 * This file implements the client interfaces of the IBMF.
 */

#include <sys/ib/mgt/ibmf/ibmf_impl.h>
#include <sys/ib/mgt/ib_mad.h>

extern ibmf_state_t *ibmf_statep;

/* global settable */
int     ibmf_send_wqes_per_port = IBMF_MAX_SQ_WRE;
int     ibmf_recv_wqes_per_port = IBMF_MAX_RQ_WRE;
int     ibmf_send_wqes_posted_per_qp = IBMF_MAX_POSTED_SQ_PER_QP;
int     ibmf_recv_wqes_posted_per_qp = IBMF_MAX_POSTED_RQ_PER_QP;

int     ibmf_taskq_max_tasks = 1024;

int     ibmf_trace_level = DPRINT_L0;

#define IBMF_MAD_CL_HDR_OFF_1   0
#define IBMF_MAD_CL_HDR_OFF_2   12
#define IBMF_MAD_CL_HDR_SZ_1    40
#define IBMF_MAD_CL_HDR_SZ_2    20
#define IBMF_MAD_CL_HDR_SZ_3    0
#define IBMF_MAD_CL_HDR_SZ_4    4

#define IBMF_VALID_CLIENT_TYPE(client_type)             \
        ((client_type) == SUBN_AGENT ||                 \
        (client_type) == SUBN_MANAGER ||                \
        (client_type) == SUBN_ADM_AGENT ||              \
        (client_type) == SUBN_ADM_MANAGER ||            \
        (client_type) == PERF_AGENT ||                  \
        (client_type) == PERF_MANAGER ||                \
        (client_type) == BM_AGENT ||                    \
        (client_type) == BM_MANAGER ||                  \
        (client_type) == DEV_MGT_AGENT ||               \
        (client_type) == DEV_MGT_MANAGER ||             \
        (client_type) == COMM_MGT_MANAGER_AGENT ||      \
        (client_type) == SNMP_MANAGER_AGENT ||          \
        (client_type) == VENDOR_09_MANAGER_AGENT ||     \
        (client_type) == VENDOR_0A_MANAGER_AGENT ||     \
        (client_type) == VENDOR_0B_MANAGER_AGENT ||     \
        (client_type) == VENDOR_0C_MANAGER_AGENT ||     \
        (client_type) == VENDOR_0D_MANAGER_AGENT ||     \
        (client_type) == VENDOR_0E_MANAGER_AGENT ||     \
        (client_type) == VENDOR_0F_MANAGER_AGENT ||     \
        (client_type) == VENDOR_30_MANAGER_AGENT ||     \
        (client_type) == VENDOR_31_MANAGER_AGENT ||     \
        (client_type) == VENDOR_32_MANAGER_AGENT ||     \
        (client_type) == VENDOR_33_MANAGER_AGENT ||     \
        (client_type) == VENDOR_34_MANAGER_AGENT ||     \
        (client_type) == VENDOR_35_MANAGER_AGENT ||     \
        (client_type) == VENDOR_36_MANAGER_AGENT ||     \
        (client_type) == VENDOR_37_MANAGER_AGENT ||     \
        (client_type) == VENDOR_38_MANAGER_AGENT ||     \
        (client_type) == VENDOR_39_MANAGER_AGENT ||     \
        (client_type) == VENDOR_3A_MANAGER_AGENT ||     \
        (client_type) == VENDOR_3B_MANAGER_AGENT ||     \
        (client_type) == VENDOR_3C_MANAGER_AGENT ||     \
        (client_type) == VENDOR_3D_MANAGER_AGENT ||     \
        (client_type) == VENDOR_3E_MANAGER_AGENT ||     \
        (client_type) == VENDOR_3F_MANAGER_AGENT ||     \
        (client_type) == VENDOR_40_MANAGER_AGENT ||     \
        (client_type) == VENDOR_41_MANAGER_AGENT ||     \
        (client_type) == VENDOR_42_MANAGER_AGENT ||     \
        (client_type) == VENDOR_43_MANAGER_AGENT ||     \
        (client_type) == VENDOR_44_MANAGER_AGENT ||     \
        (client_type) == VENDOR_45_MANAGER_AGENT ||     \
        (client_type) == VENDOR_46_MANAGER_AGENT ||     \
        (client_type) == VENDOR_47_MANAGER_AGENT ||     \
        (client_type) == VENDOR_48_MANAGER_AGENT ||     \
        (client_type) == VENDOR_49_MANAGER_AGENT ||     \
        (client_type) == VENDOR_4A_MANAGER_AGENT ||     \
        (client_type) == VENDOR_4B_MANAGER_AGENT ||     \
        (client_type) == VENDOR_4C_MANAGER_AGENT ||     \
        (client_type) == VENDOR_4D_MANAGER_AGENT ||     \
        (client_type) == VENDOR_4E_MANAGER_AGENT ||     \
        (client_type) == VENDOR_4F_MANAGER_AGENT ||     \
        (client_type) == APPLICATION_10_MANAGER_AGENT || \
        (client_type) == APPLICATION_11_MANAGER_AGENT || \
        (client_type) == APPLICATION_12_MANAGER_AGENT || \
        (client_type) == APPLICATION_13_MANAGER_AGENT || \
        (client_type) == APPLICATION_14_MANAGER_AGENT || \
        (client_type) == APPLICATION_15_MANAGER_AGENT || \
        (client_type) == APPLICATION_16_MANAGER_AGENT || \
        (client_type) == APPLICATION_17_MANAGER_AGENT || \
        (client_type) == APPLICATION_18_MANAGER_AGENT || \
        (client_type) == APPLICATION_19_MANAGER_AGENT || \
        (client_type) == APPLICATION_1A_MANAGER_AGENT || \
        (client_type) == APPLICATION_1B_MANAGER_AGENT || \
        (client_type) == APPLICATION_1C_MANAGER_AGENT || \
        (client_type) == APPLICATION_1D_MANAGER_AGENT || \
        (client_type) == APPLICATION_1E_MANAGER_AGENT || \
        (client_type) == APPLICATION_1F_MANAGER_AGENT || \
        (client_type) == APPLICATION_20_MANAGER_AGENT || \
        (client_type) == APPLICATION_21_MANAGER_AGENT || \
        (client_type) == APPLICATION_22_MANAGER_AGENT || \
        (client_type) == APPLICATION_23_MANAGER_AGENT || \
        (client_type) == APPLICATION_24_MANAGER_AGENT || \
        (client_type) == APPLICATION_25_MANAGER_AGENT || \
        (client_type) == APPLICATION_26_MANAGER_AGENT || \
        (client_type) == APPLICATION_27_MANAGER_AGENT || \
        (client_type) == APPLICATION_28_MANAGER_AGENT || \
        (client_type) == APPLICATION_29_MANAGER_AGENT || \
        (client_type) == APPLICATION_2A_MANAGER_AGENT || \
        (client_type) == APPLICATION_2B_MANAGER_AGENT || \
        (client_type) == APPLICATION_2C_MANAGER_AGENT || \
        (client_type) == APPLICATION_2D_MANAGER_AGENT || \
        (client_type) == APPLICATION_2E_MANAGER_AGENT || \
        (client_type) == APPLICATION_2F_MANAGER_AGENT || \
        (client_type) == UNIVERSAL_CLASS)

static ibmf_ci_t *ibmf_i_lookup_ci(ib_guid_t ci_guid);
static int ibmf_i_init_ci(ibmf_register_info_t *client_infop,
    ibmf_ci_t *cip);
static void ibmf_i_uninit_ci(ibmf_ci_t *cip);
static void ibmf_i_init_ci_done(ibmf_ci_t *cip);
static void ibmf_i_uninit_ci_done(ibmf_ci_t *cip);
static int ibmf_i_init_qp(ibmf_ci_t *ibmf_cip, ibmf_qp_t *qpp);
static void ibmf_i_uninit_qp(ibmf_ci_t *ibmf_cip, ibmf_qp_t *qpp);
static int ibmf_i_init_cqs(ibmf_ci_t *cip);
static void ibmf_i_fini_cqs(ibmf_ci_t *cip);
static void ibmf_i_init_qplist(ibmf_ci_t *ibmf_cip);
static void ibmf_i_fini_qplist(ibmf_ci_t *ibmf_cip);
static int ibmf_i_lookup_client_by_info(ibmf_ci_t *ibmf_cip,
    ibmf_register_info_t *ir_client, ibmf_client_t **clientpp);

/*
 * ibmf_init():
 *      Initializes module state and registers with the IBT framework.
 *      Returns 0 if initialization was successful, else returns non-zero.
 */
int
ibmf_init(void)
{
        ibt_status_t    status;
        ibt_clnt_hdl_t  ibmf_ibt_handle;

        IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_init_start,
            IBMF_TNF_TRACE, "", "ibmf_init() enter\n");

        /* setup the IBT module information */
        ibmf_statep->ibmf_ibt_modinfo.mi_ibt_version = IBTI_V_CURR;
        ibmf_statep->ibmf_ibt_modinfo.mi_clnt_class = IBT_IBMA;
        ibmf_statep->ibmf_ibt_modinfo.mi_async_handler
            = ibmf_ibt_async_handler;
        ibmf_statep->ibmf_ibt_modinfo.mi_reserved = NULL;
        ibmf_statep->ibmf_ibt_modinfo.mi_clnt_name = "ibmf";

        /* setup a connection to IB transport layer (IBTF) */
        status = ibt_attach(&ibmf_statep->ibmf_ibt_modinfo, (void *)NULL,
            (void *)NULL, (void *)&ibmf_ibt_handle);
        if (status != IBT_SUCCESS) {
                IBMF_TRACE_2(IBMF_TNF_NODEBUG, DPRINT_L1, ibmf_init_err,
                    IBMF_TNF_ERROR, "", "%s, status = %d\n", tnf_string, msg,
                    "ibt attach failed", tnf_uint, status, status);
                IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_init_end,
                    IBMF_TNF_TRACE, "", "ibmf_init() exit\n");
                return (1);
        }

        /* initialize the IBMF state context */
        ibmf_statep->ibmf_ibt_handle = ibmf_ibt_handle;
        ibmf_statep->ibmf_ci_list = (ibmf_ci_t *)NULL;
        ibmf_statep->ibmf_ci_list_tail = (ibmf_ci_t *)NULL;
        mutex_init(&ibmf_statep->ibmf_mutex, NULL, MUTEX_DRIVER, NULL);
        ibmf_statep->ibmf_cq_handler = ibmf_i_mad_completions;

        ibmf_statep->ibmf_taskq = taskq_create("ibmf_taskq", IBMF_TASKQ_1THREAD,
            MINCLSYSPRI, 1, ibmf_taskq_max_tasks, TASKQ_PREPOPULATE);

        IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_init_end,
            IBMF_TNF_TRACE, "", "ibmf_init() exit\n");

        return (0);
}

/*
 * ibmf_fini():
 *      Cleans up module state resources and unregisters from IBT framework.
 */
int
ibmf_fini(void)
{
        ibmf_ci_t       *cip;
        ibmf_ci_t       *tcip;
        ibt_status_t    status;

        IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_fini_start,
            IBMF_TNF_TRACE, "", "ibmf_fini() enter\n");

        ASSERT(MUTEX_NOT_HELD(&ibmf_statep->ibmf_mutex));

        mutex_enter(&ibmf_statep->ibmf_mutex);

        /* free all the Channel Interface (CI) context structures */
        cip = ibmf_statep->ibmf_ci_list;
        tcip = NULL;
        while (cip != (ibmf_ci_t *)NULL) {

                mutex_enter(&cip->ci_mutex);
                ASSERT((cip->ci_state == IBMF_CI_STATE_PRESENT && cip->ci_ref ==
                    0) || (cip->ci_state == IBMF_CI_STATE_GONE));
                ASSERT(cip->ci_init_state == IBMF_CI_INIT_HCA_LINKED);
                ASSERT(cip->ci_qp_list == NULL && cip->ci_qp_list_tail == NULL);
                if (tcip != (ibmf_ci_t *)NULL)
                        tcip->ci_next = cip->ci_next;
                if (ibmf_statep->ibmf_ci_list_tail == cip)
                        ibmf_statep->ibmf_ci_list_tail = NULL;
                if (ibmf_statep->ibmf_ci_list == cip)
                        ibmf_statep->ibmf_ci_list = cip->ci_next;
                tcip = cip->ci_next;
                mutex_exit(&cip->ci_mutex);
                /* free up the ci structure */
                if (cip->ci_port_kstatp != NULL) {
                        kstat_delete(cip->ci_port_kstatp);
                }
                mutex_destroy(&cip->ci_mutex);
                mutex_destroy(&cip->ci_clients_mutex);
                mutex_destroy(&cip->ci_wqe_mutex);
                cv_destroy(&cip->ci_state_cv);
                cv_destroy(&cip->ci_wqes_cv);
                kmem_free((void *) cip, sizeof (ibmf_ci_t));
                cip = tcip;
        }

        ASSERT(ibmf_statep->ibmf_ci_list == NULL);
        ASSERT(ibmf_statep->ibmf_ci_list_tail == NULL);

        taskq_destroy(ibmf_statep->ibmf_taskq);

        mutex_exit(&ibmf_statep->ibmf_mutex);

        /* detach from IBTF */
        status = ibt_detach(ibmf_statep->ibmf_ibt_handle);
        if (status != IBT_SUCCESS) {
                IBMF_TRACE_2(IBMF_TNF_NODEBUG, DPRINT_L1, ibmf_fini_err,
                    IBMF_TNF_ERROR, "", "%s, status = %d\n", tnf_string, msg,
                    "ibt detach error", tnf_uint, status, status);
                IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_fini_end,
                    IBMF_TNF_TRACE, "", "ibmf_fini() exit\n");
                return (1);
        }

        mutex_destroy(&ibmf_statep->ibmf_mutex);

        IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_fini_end,
            IBMF_TNF_TRACE, "", "ibmf_fini() exit\n");

        return (0);
}

/*
 * ibmf_i_validate_class_mask():
 *      Checks client type value in client information structure.
 */
int
ibmf_i_validate_class_mask(ibmf_register_info_t *client_infop)
{
        IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L4,
            ibmf_i_validate_class_mask_start, IBMF_TNF_TRACE, "",
            "ibmf_i_validate_class_mask() enter, client_infop = %p\n",
            tnf_opaque, client_infop, client_infop);

        if (IBMF_VALID_CLIENT_TYPE(client_infop->ir_client_class) == B_FALSE) {
                IBMF_TRACE_2(IBMF_TNF_NODEBUG, DPRINT_L1,
                    ibmf_i_validate_class_mask_err, IBMF_TNF_ERROR, "",
                    "%s, class = %x\n", tnf_string, msg,
                    "invalid class", tnf_uint, class,
                    client_infop->ir_client_class);
                IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
                    ibmf_i_validate_class_mask_end, IBMF_TNF_TRACE, "",
                    "ibmf_i_validate_class_mask() exit\n");
                return (IBMF_BAD_CLASS);
        }

        IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_validate_class_mask_end,
            IBMF_TNF_TRACE, "", "ibmf_i_validate_class_mask() exit\n");
        return (IBMF_SUCCESS);
}

/*
 * ibmf_i_validate_ci_guid_and_port():
 *      Checks validity of port number and HCA GUID at client
 *      registration time.
 */
int
ibmf_i_validate_ci_guid_and_port(ib_guid_t hca_guid, uint8_t port_num)
{
        ibt_status_t    status;
        ibt_hca_attr_t  hca_attrs;

        IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L4,
            ibmf_i_validate_ci_guid_and_port_start, IBMF_TNF_TRACE, "",
            "ibmf_i_validate_ci_guid_and_port() enter, hca_guid = %x, "
            "port_num = %d\n", tnf_opaque, hca_guid, hca_guid,
            tnf_uint, port_num, port_num);

        /* check for incorrect port number specification */
        if (port_num == 0) {
                IBMF_TRACE_1(IBMF_TNF_NODEBUG, 1,
                    ibmf_i_validate_ci_guid_and_port_err, IBMF_TNF_ERROR, "",
                    "%s\n", tnf_string, msg, "port num is 0");
                IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
                    ibmf_i_validate_ci_guid_and_port_end, IBMF_TNF_TRACE, "",
                    "ibmf_i_validate_ci_guid_and_port() exit\n");
                return (IBMF_BAD_PORT);
        }

        /* call IB transport layer for HCA attributes */
        status = ibt_query_hca_byguid(hca_guid, &hca_attrs);
        if (status != IBT_SUCCESS) {
                IBMF_TRACE_2(IBMF_TNF_NODEBUG, DPRINT_L1,
                    ibmf_i_validate_ci_guid_and_port_err,
                    IBMF_TNF_ERROR, "", "%s, status = %d\n", tnf_string, msg,
                    "query_hca_guid failed", tnf_uint, status, status);
                IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
                    ibmf_i_validate_ci_guid_and_port_end, IBMF_TNF_TRACE, "",
                    "ibmf_i_validate_ci_guid_and_port() exit\n");
                return (IBMF_BAD_NODE);
        }

        /* check if the specified port number is within the HCAs range */
        if (port_num > hca_attrs.hca_nports) {
                IBMF_TRACE_3(IBMF_TNF_NODEBUG, 1,
                    ibmf_i_validate_ci_guid_and_port_err, IBMF_TNF_ERROR, "",
                    "%s, num = %d, hca_ports = %d\n",
                    tnf_string, msg, "port num > valid ports",
                    tnf_uint, num, port_num, tnf_uint, hca_nports,
                    hca_attrs.hca_nports);
                IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
                    ibmf_i_validate_ci_guid_and_port_end, IBMF_TNF_TRACE, "",
                    "ibmf_i_validate_ci_guid_and_port() exit\n");
                return (IBMF_BAD_PORT);
        }

        IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
            ibmf_i_validate_ci_guid_and_port_end, IBMF_TNF_TRACE, "",
            "ibmf_i_validate_ci_guid_and_port() exit\n");
        return (IBMF_SUCCESS);
}

/*
 * ibmf_i_lookup_ci():
 *      Lookup the ci and return if found. If the CI is not found, returns
 *      NULL.
 */
static ibmf_ci_t *
ibmf_i_lookup_ci(ib_guid_t ci_guid)
{
        ibmf_ci_t       *cip = NULL;

        ASSERT(MUTEX_NOT_HELD(&ibmf_statep->ibmf_mutex));

        IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_lookup_ci_start,
            IBMF_TNF_TRACE, "", "ibmf_i_lookup_ci(): enter, guid = 0x%x\n",
            tnf_uint64, guid, ci_guid);

        /* walk the CI list looking for one that matches the provided GUID */
        mutex_enter(&ibmf_statep->ibmf_mutex);
        cip = ibmf_statep->ibmf_ci_list;
        while (cip != (ibmf_ci_t *)NULL) {
                if (ci_guid == cip->ci_node_guid) {
                        /* found it in our list */
                        break;
                }
                cip = cip->ci_next;
        }
        mutex_exit(&ibmf_statep->ibmf_mutex);

        IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_lookup_ci_end,
            IBMF_TNF_TRACE, "", "ibmf_i_lookup_ci() exit\n");

        return (cip);
}

/*
 * ibmf_i_get_ci():
 *      Get the CI structure based on the HCA GUID from a list if it exists.
 *      If the CI structure does not exist, and the HCA GUID is valid,
 *      create a new CI structure and add it to the list.
 */
int
ibmf_i_get_ci(ibmf_register_info_t *client_infop, ibmf_ci_t **cipp)
{
        ibmf_ci_t               *cip;
        ibt_status_t            status;
        boolean_t               invalid = B_FALSE;
        ibt_hca_attr_t          hca_attrs;
        ibmf_port_kstat_t       *ksp;

        IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_get_ci_start,
            IBMF_TNF_TRACE, "", "ibmf_i_get_ci() enter, clinfop = %p\n",
            tnf_opaque, client_infop, client_infop);

        /* look for a CI context with a matching GUID */
        cip = ibmf_i_lookup_ci(client_infop->ir_ci_guid);

        if (cip == NULL) {

                /*
                 * attempt to create the ci. First, verify the ci exists.
                 * If it exists, allocate ci memory and insert in the ci list.
                 * It is possible that some other thread raced with us
                 * and inserted created ci while we are blocked in
                 * allocating memory. Check for that case and if that is indeed
                 * the case, free up what we allocated and try to get a
                 * reference count on the ci that the other thread added.
                 */
                status = ibt_query_hca_byguid(client_infop->ir_ci_guid,
                    &hca_attrs);
                if (status == IBT_SUCCESS) {

                        ibmf_ci_t *tcip;
                        char buf[128];

                        /* allocate memory for the CI structure */
                        cip = (ibmf_ci_t *)kmem_zalloc(sizeof (ibmf_ci_t),
                            KM_SLEEP);

                        _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*cip))

                        mutex_init(&cip->ci_mutex, NULL, MUTEX_DRIVER, NULL);
                        mutex_init(&cip->ci_clients_mutex, NULL, MUTEX_DRIVER,
                            NULL);
                        mutex_init(&cip->ci_wqe_mutex, NULL, MUTEX_DRIVER,
                            NULL);
                        cv_init(&cip->ci_state_cv, NULL, CV_DRIVER, NULL);
                        cv_init(&cip->ci_wqes_cv, NULL, CV_DRIVER, NULL);

                        (void) sprintf(buf, "r%08X",
                            (uint32_t)client_infop->ir_ci_guid);
                        mutex_enter(&cip->ci_mutex);

                        cip->ci_state = IBMF_CI_STATE_PRESENT;
                        cip->ci_node_guid = client_infop->ir_ci_guid;

                        /* set up per CI kstats */
                        (void) sprintf(buf, "ibmf_%016" PRIx64 "_%d_stat",
                            client_infop->ir_ci_guid,
                            client_infop->ir_port_num);
                        if ((cip->ci_port_kstatp = kstat_create("ibmf", 0, buf,
                            "misc", KSTAT_TYPE_NAMED,
                            sizeof (ibmf_port_kstat_t) / sizeof (kstat_named_t),
                            KSTAT_FLAG_WRITABLE)) == NULL) {
                                mutex_exit(&cip->ci_mutex);
                                mutex_destroy(&cip->ci_mutex);
                                mutex_destroy(&cip->ci_clients_mutex);
                                mutex_destroy(&cip->ci_wqe_mutex);
                                cv_destroy(&cip->ci_state_cv);
                                cv_destroy(&cip->ci_wqes_cv);
                                kmem_free((void *)cip, sizeof (ibmf_ci_t));
                                IBMF_TRACE_1(IBMF_TNF_NODEBUG, DPRINT_L1,
                                    ibmf_i_get_ci_err, IBMF_TNF_ERROR, "",
                                    "%s\n", tnf_string, msg,
                                    "kstat create failed");
                                IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
                                    ibmf_i_get_ci_end, IBMF_TNF_TRACE, "",
                                    "ibmf_i_get_ci() exit\n");
                                return (IBMF_NO_RESOURCES);
                        }
                        ksp = (ibmf_port_kstat_t *)cip->ci_port_kstatp->ks_data;
                        kstat_named_init(&ksp->clients_registered,
                            "clients_registered", KSTAT_DATA_UINT32);
                        kstat_named_init(&ksp->client_regs_failed,
                            "client_registrations_failed", KSTAT_DATA_UINT32);
                        kstat_named_init(&ksp->send_wqes_alloced,
                            "send_wqes_allocated", KSTAT_DATA_UINT32);
                        kstat_named_init(&ksp->recv_wqes_alloced,
                            "receive_wqes_allocated", KSTAT_DATA_UINT32);
                        kstat_named_init(&ksp->swqe_allocs_failed,
                            "send_wqe_allocs_failed", KSTAT_DATA_UINT32);
                        kstat_named_init(&ksp->rwqe_allocs_failed,
                            "recv_wqe_allocs_failed", KSTAT_DATA_UINT32);
                        kstat_install(cip->ci_port_kstatp);

                        mutex_exit(&cip->ci_mutex);

                        mutex_enter(&ibmf_statep->ibmf_mutex);

                        tcip = ibmf_statep->ibmf_ci_list;
                        while (tcip != (ibmf_ci_t *)NULL) {
                                if (client_infop->ir_ci_guid ==
                                    tcip->ci_node_guid) {
                                        /* found it in our list */
                                        break;
                                }
                                tcip = tcip->ci_next;
                        }

                        /* if the ci isn't on the list, add it */
                        if (tcip == NULL) {
                                cip->ci_next = NULL;

                                _NOTE(NOW_VISIBLE_TO_OTHER_THREADS(*cip))

                                if (ibmf_statep->ibmf_ci_list_tail != NULL)
                                        ibmf_statep->ibmf_ci_list_tail->
                                            ci_next = cip;
                                if (ibmf_statep->ibmf_ci_list == NULL)
                                        ibmf_statep->ibmf_ci_list = cip;
                                ibmf_statep->ibmf_ci_list_tail = cip;

                                mutex_enter(&cip->ci_mutex);
                                cip->ci_init_state |= IBMF_CI_INIT_HCA_LINKED;
                                mutex_exit(&cip->ci_mutex);

                        } else {
                                /* free cip and set it to the one on the list */
                                kstat_delete(cip->ci_port_kstatp);
                                mutex_destroy(&cip->ci_mutex);
                                mutex_destroy(&cip->ci_clients_mutex);
                                mutex_destroy(&cip->ci_wqe_mutex);
                                cv_destroy(&cip->ci_state_cv);
                                cv_destroy(&cip->ci_wqes_cv);
                                kmem_free((void *)cip, sizeof (ibmf_ci_t));

                                _NOTE(NOW_VISIBLE_TO_OTHER_THREADS(*cip))

                                cip = tcip;
                        }
                        mutex_exit(&ibmf_statep->ibmf_mutex);
                } else {
                        /* we didn't find it and the CI doesn't exist */
                        IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L1,
                            ibmf_i_get_ci_err, IBMF_TNF_ERROR, "", "%s\n",
                            tnf_string, msg, "GUID doesn't exist");
                        IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
                            ibmf_i_get_ci_end, IBMF_TNF_TRACE, "",
                            "ibmf_i_get_ci() exit\n");
                        return (IBMF_TRANSPORT_FAILURE);
                }
        }

        ASSERT(cip != NULL);

        /*
         * We now have a CI context structure, either found it on the list,
         * or created it.
         * We now proceed to intialize the CI context.
         */
        for (;;) {
                mutex_enter(&cip->ci_mutex);

                /* CI is INITED & no state change in progress; we are all set */
                if (cip->ci_state == IBMF_CI_STATE_INITED && (cip->
                    ci_state_flags & (IBMF_CI_STATE_INVALIDATING |
                    IBMF_CI_STATE_UNINITING)) == 0) {

                        cip->ci_ref++;
                        mutex_exit(&cip->ci_mutex);

                        break;
                }

                /* CI is PRESENT; transition it to INITED */
                if (cip->ci_state == IBMF_CI_STATE_PRESENT && (cip->
                    ci_state_flags & (IBMF_CI_STATE_INVALIDATING |
                    IBMF_CI_STATE_INITING)) == 0) {

                        /* mark state as initing and init the ci */
                        cip->ci_state_flags |= IBMF_CI_STATE_INITING;
                        mutex_exit(&cip->ci_mutex);

                        _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*cip))

                        if (ibmf_i_init_ci(client_infop, cip) != IBMF_SUCCESS) {
                                invalid = B_TRUE;
                                break;
                        }

                        _NOTE(NOW_VISIBLE_TO_OTHER_THREADS(*cip))

                        continue;
                }

                /*
                 * If CI is GONE and no validation is in progress, we should
                 * return failure. Also, if CI is INITED but in the process of
                 * being made GONE (ie., a hot remove in progress), return
                 * failure.
                 */
                if ((cip->ci_state == IBMF_CI_STATE_GONE && (cip->
                    ci_state_flags & IBMF_CI_STATE_VALIDATING) == 0) ||
                    (cip->ci_state == IBMF_CI_STATE_INITED && (cip->
                    ci_state_flags & IBMF_CI_STATE_INVALIDATING) != 0)) {

                        IBMF_TRACE_2(IBMF_TNF_NODEBUG, DPRINT_L1,
                            ibmf_i_get_ci_err, IBMF_TNF_ERROR, "",
                            "ci_state = %x, ci_state_flags = %x\n",
                            tnf_opaque, cip->ci_state, cip->ci_state,
                            tnf_opaque, cip->ci_state_flags,
                            cip->ci_state_flags);

                        invalid = B_TRUE;
                        mutex_exit(&cip->ci_mutex);

                        break;
                }

                /* a state change in progress; block waiting for state change */
                if (cip->ci_state_flags & IBMF_CI_STATE_VALIDATING)
                        cip->ci_state_flags |= IBMF_CI_STATE_VALIDATE_WAIT;
                else if (cip->ci_state_flags & IBMF_CI_STATE_INITING)
                        cip->ci_state_flags |= IBMF_CI_STATE_INIT_WAIT;
                else if (cip->ci_state_flags & IBMF_CI_STATE_UNINITING)
                        cip->ci_state_flags |= IBMF_CI_STATE_UNINIT_WAIT;

                cv_wait(&cip->ci_state_cv, &cip->ci_mutex);

                mutex_exit(&cip->ci_mutex);
        }

        if (invalid == B_TRUE) {
                IBMF_TRACE_0(IBMF_TNF_NODEBUG, DPRINT_L2, ibmf_i_get_ci_err,
                    IBMF_TNF_ERROR, "", "ibmf_i_get_ci() error\n");
                IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_get_ci_end,
                    IBMF_TNF_TRACE, "", "ibmf_i_get_ci() exit\n");
                return (IBMF_FAILURE);
        }

        if (cip != NULL) {
                *cipp = cip;
                IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_get_ci_end,
                    IBMF_TNF_TRACE, "", "ibmf_i_get_ci() exit\n");
                return (IBMF_SUCCESS);
        } else {
                IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_get_ci_end,
                    IBMF_TNF_TRACE, "", "ibmf_i_get_ci() exit\n");
                return (IBMF_FAILURE);
        }
}

/*
 * ibmf_i_release_ci():
 *      Drop the reference count for the CI.
 */
void
ibmf_i_release_ci(ibmf_ci_t *cip)
{
        uint_t ref;

        IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_release_ci_start,
            IBMF_TNF_TRACE, "", "ibmf_i_release_ci() enter, cip = %p\n",
            tnf_opaque, cip, cip);

        ASSERT(MUTEX_NOT_HELD(&cip->ci_mutex));

        mutex_enter(&cip->ci_mutex);
        ref = cip->ci_ref--;
        if (ref == 1) {
                ASSERT(cip->ci_state == IBMF_CI_STATE_INITED);
                cip->ci_state_flags |= IBMF_CI_STATE_UNINITING;
        }
        mutex_exit(&cip->ci_mutex);

        if (ref == 1) {
                ibmf_i_uninit_ci(cip);
        }

        IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_release_ci_end,
            IBMF_TNF_TRACE, "", "ibmf_i_release_ci() exit\n");
}

/*
 * ibmf_i_init_ci():
 *      Initialize the CI structure by setting up the HCA, allocating
 *      protection domains, completion queues, a pool of WQEs.
 */
/* ARGSUSED */
static int
ibmf_i_init_ci(ibmf_register_info_t *client_infop, ibmf_ci_t *cip)
{
        ibt_pd_hdl_t            pd;
        ibt_status_t            status;
        ib_guid_t               ci_guid;
        ibt_hca_attr_t          hca_attrs;
        ibt_hca_hdl_t           hca_handle;
        ibt_pd_flags_t          pd_flags = IBT_PD_NO_FLAGS;
        boolean_t               error = B_FALSE;
        int                     ibmfstatus = IBMF_SUCCESS;
        char                    errmsg[128];

        _NOTE(ASSUMING_PROTECTED(*cip))

        ASSERT(MUTEX_NOT_HELD(&ibmf_statep->ibmf_mutex));
        ASSERT(MUTEX_NOT_HELD(&cip->ci_mutex));

        IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_init_ci_start,
            IBMF_TNF_TRACE, "", "ibmf_i_init_ci() enter, cip = %p\n",
            tnf_opaque, ibmf_ci, cip);

        mutex_enter(&cip->ci_mutex);
        ci_guid = cip->ci_node_guid;
        ASSERT(cip->ci_state == IBMF_CI_STATE_PRESENT);
        ASSERT((cip->ci_state_flags & IBMF_CI_STATE_INITING) != 0);
        mutex_exit(&cip->ci_mutex);

        /* set up a connection to the HCA specified by the GUID */
        status = ibt_open_hca(ibmf_statep->ibmf_ibt_handle, ci_guid,
            &hca_handle);
        ASSERT(status != IBT_HCA_IN_USE);
        if (status != IBT_SUCCESS) {
                ibmf_i_init_ci_done(cip);
                (void) sprintf(errmsg, "ibt open hca failed, status = 0x%x",
                    status);
                error = B_TRUE;
                ibmfstatus = IBMF_TRANSPORT_FAILURE;
                goto bail;
        }

        /* get the HCA attributes */
        status = ibt_query_hca(hca_handle, &hca_attrs);
        if (status != IBT_SUCCESS) {
                (void) ibt_close_hca(hca_handle);
                ibmf_i_init_ci_done(cip);
                (void) sprintf(errmsg, "ibt query hca failed, status = 0x%x",
                    status);
                error = B_TRUE;
                ibmfstatus = IBMF_TRANSPORT_FAILURE;
                goto bail;
        }

        /* allocate a Protection Domain */
        status = ibt_alloc_pd(hca_handle, pd_flags, &pd);
        if (status != IBT_SUCCESS) {
                (void) ibt_close_hca(hca_handle);
                ibmf_i_init_ci_done(cip);
                (void) sprintf(errmsg, "alloc PD failed, status = 0x%x",
                    status);
                error = B_TRUE;
                ibmfstatus = IBMF_TRANSPORT_FAILURE;
                goto bail;
        }

        /* init the ci */
        mutex_enter(&cip->ci_mutex);
        cip->ci_nports = hca_attrs.hca_nports;
        cip->ci_vendor_id = hca_attrs.hca_vendor_id;
        cip->ci_device_id = hca_attrs.hca_device_id;
        cip->ci_ci_handle = hca_handle;
        cip->ci_pd = pd;
        cip->ci_init_state |= IBMF_CI_INIT_HCA_INITED;
        mutex_exit(&cip->ci_mutex);

        /* initialize cqs */
        if (ibmf_i_init_cqs(cip) != IBMF_SUCCESS) {
                (void) ibt_free_pd(cip->ci_ci_handle, cip->ci_pd);
                mutex_enter(&cip->ci_mutex);
                cip->ci_init_state &= ~IBMF_CI_INIT_HCA_INITED;
                mutex_exit(&cip->ci_mutex);
                (void) ibt_close_hca(cip->ci_ci_handle);
                ibmf_i_init_ci_done(cip);
                (void) sprintf(errmsg, "init CQs failed");
                error = B_TRUE;
                ibmfstatus = IBMF_FAILURE;
                goto bail;
        }

        /* initialize wqes */
        if (ibmf_i_init_wqes(cip) != IBMF_SUCCESS) {
                ibmf_i_fini_cqs(cip);
                (void) ibt_free_pd(cip->ci_ci_handle, cip->ci_pd);
                mutex_enter(&cip->ci_mutex);
                cip->ci_init_state &= ~IBMF_CI_INIT_HCA_INITED;
                mutex_exit(&cip->ci_mutex);
                (void) ibt_close_hca(cip->ci_ci_handle);
                ibmf_i_init_ci_done(cip);
                (void) sprintf(errmsg, "init WQEs failed");
                error = B_TRUE;
                ibmfstatus = IBMF_FAILURE;
                goto bail;
        }

        /* initialize the UD destination structure pool */
        ibmf_i_init_ud_dest(cip);

        /* initialize the QP list */
        ibmf_i_init_qplist(cip);

        /* initialize condition variable, state, and enable CQ notification */
        cip->ci_init_state |= IBMF_CI_INIT_MUTEX_CV_INITED;
        (void) ibt_enable_cq_notify(cip->ci_cq_handle, IBT_NEXT_COMPLETION);
        (void) ibt_enable_cq_notify(cip->ci_alt_cq_handle, IBT_NEXT_COMPLETION);

        /* set state to INITED */
        mutex_enter(&cip->ci_mutex);
        cip->ci_state = IBMF_CI_STATE_INITED;
        mutex_exit(&cip->ci_mutex);

        /* wake up waiters blocked on an initialization done event */
        ibmf_i_init_ci_done(cip);

bail:
        if (error) {
                IBMF_TRACE_2(IBMF_TNF_NODEBUG, DPRINT_L1, ibmf_i_init_ci_err,
                    IBMF_TNF_ERROR, "", "%s, status = %d\n", tnf_string, msg,
                    errmsg, tnf_uint, ibmfstatus, ibmfstatus);
        }

        IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_init_ci_end,
            IBMF_TNF_TRACE, "", "ibmf_i_init_ci() exit, cip = %p\n",
            tnf_opaque, ibmf_ci, cip);

        return (ibmfstatus);
}

/*
 * ibmf_i_uninit_ci():
 *      Free up the resources allocated when initializing the CI structure.
 */
static void
ibmf_i_uninit_ci(ibmf_ci_t *cip)
{
        IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_uninit_ci_start,
            IBMF_TNF_TRACE, "", "ibmf_i_uninit_ci() enter, cip = %p\n",
            tnf_opaque, cip, cip);

        ASSERT(MUTEX_HELD(&cip->ci_mutex) == 0);

        /* clean up the QP list */
        ibmf_i_fini_qplist(cip);

        /* empty completions directly */
        ibmf_i_mad_completions(cip->ci_cq_handle, (void*)cip);
        ibmf_i_mad_completions(cip->ci_alt_cq_handle, (void*)cip);

        mutex_enter(&cip->ci_mutex);
        if (cip->ci_init_state & IBMF_CI_INIT_MUTEX_CV_INITED) {
                cip->ci_init_state &= ~IBMF_CI_INIT_MUTEX_CV_INITED;
        }
        mutex_exit(&cip->ci_mutex);

        /* clean up the UD destination structure pool */
        ibmf_i_fini_ud_dest(cip);

        /* clean up any WQE caches */
        ibmf_i_fini_wqes(cip);

        /* free up the completion queues */
        ibmf_i_fini_cqs(cip);

        /* free up the protection domain */
        (void) ibt_free_pd(cip->ci_ci_handle, cip->ci_pd);

        /* close the HCA connection */
        (void) ibt_close_hca(cip->ci_ci_handle);

        /* set state down to PRESENT */
        mutex_enter(&cip->ci_mutex);
        cip->ci_init_state &= ~IBMF_CI_INIT_HCA_INITED;
        cip->ci_state = IBMF_CI_STATE_PRESENT;
        mutex_exit(&cip->ci_mutex);

        /* wake up waiters blocked on an un-initialization done event */
        ibmf_i_uninit_ci_done(cip);

        IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_uninit_ci_end,
            IBMF_TNF_TRACE, "", "ibmf_i_uninit_ci() exit\n");
}

/*
 * ibmf_i_init_ci_done():
 *      Mark CI initialization as "done", and wake up any waiters.
 */
static void
ibmf_i_init_ci_done(ibmf_ci_t *cip)
{
        IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_init_ci_done_start,
            IBMF_TNF_TRACE, "", "ibmf_i_init_ci_done() enter, cip = %p\n",
            tnf_opaque, cip, cip);

        mutex_enter(&cip->ci_mutex);
        cip->ci_state_flags &= ~IBMF_CI_STATE_INITING;
        if (cip->ci_state_flags & IBMF_CI_STATE_INIT_WAIT) {
                cip->ci_state_flags &= ~IBMF_CI_STATE_INIT_WAIT;
                cv_broadcast(&cip->ci_state_cv);
        }
        mutex_exit(&cip->ci_mutex);

        IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_init_ci_done_end,
            IBMF_TNF_TRACE, "", "ibmf_i_init_ci_done() exit\n");
}

/*
 * ibmf_i_uninit_ci_done():
 *      Mark CI uninitialization as "done", and wake up any waiters.
 */
static void
ibmf_i_uninit_ci_done(ibmf_ci_t *cip)
{
        IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_uninit_ci_done_start,
            IBMF_TNF_TRACE, "", "ibmf_i_uninit_ci_done() enter, cip = %p\n",
            tnf_opaque, cip, cip);

        mutex_enter(&cip->ci_mutex);
        cip->ci_state_flags &= ~IBMF_CI_STATE_UNINITING;
        if (cip->ci_state_flags & IBMF_CI_STATE_UNINIT_WAIT) {
                cip->ci_state_flags &= ~IBMF_CI_STATE_UNINIT_WAIT;
                cv_broadcast(&cip->ci_state_cv);
        }
        mutex_exit(&cip->ci_mutex);

        IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_uninit_ci_done_end,
            IBMF_TNF_TRACE, "", "ibmf_i_uninit_ci_done() exit\n");
}

/*
 * ibmf_i_init_cqs():
 *      Allocate a completion queue and set the CQ handler.
 */
static int
ibmf_i_init_cqs(ibmf_ci_t *cip)
{
        ibt_status_t            status;
        ibt_cq_attr_t           cq_attrs;
        ibt_cq_hdl_t            cq_handle;
        uint32_t                num_entries;

        ASSERT(MUTEX_NOT_HELD(&cip->ci_mutex));

        IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_init_cqs_start,
            IBMF_TNF_TRACE, "", "ibmf_i_init_cqs() enter, cip = %p\n",
            tnf_opaque, cip, cip);

        /*
         * Allocate completion queue handle.
         * The CQ size should be a 2^n - 1 value to avoid excess CQ allocation
         * as done by some HCAs when the CQ size is specified as a 2^n
         * quantity.
         */
        cq_attrs.cq_size = (cip->ci_nports * (ibmf_send_wqes_posted_per_qp +
            ibmf_recv_wqes_posted_per_qp)) - 1;

        cq_attrs.cq_sched = NULL;
        cq_attrs.cq_flags = 0;

        /* Get the CQ handle for the special QPs */
        status = ibt_alloc_cq(cip->ci_ci_handle, &cq_attrs,
            &cq_handle, &num_entries);
        if (status != IBT_SUCCESS) {
                IBMF_TRACE_2(IBMF_TNF_NODEBUG, DPRINT_L1, ibmf_i_init_cqs_err,
                    IBMF_TNF_ERROR, "", "%s, status = %d\n", tnf_string, msg,
                    "ibt_alloc_cq failed", tnf_uint, ibt_status, status);
                IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_init_cqs_end,
                    IBMF_TNF_TRACE, "", "ibmf_i_init_cqs() exit\n");
                return (IBMF_TRANSPORT_FAILURE);
        }
        ibt_set_cq_handler(cq_handle, ibmf_statep->ibmf_cq_handler, cip);
        cip->ci_cq_handle = cq_handle;

        /* Get the CQ handle for the alternate QPs */
        status = ibt_alloc_cq(cip->ci_ci_handle, &cq_attrs,
            &cq_handle, &num_entries);
        if (status != IBT_SUCCESS) {
                (void) ibt_free_cq(cip->ci_cq_handle);
                IBMF_TRACE_2(IBMF_TNF_NODEBUG, DPRINT_L1, ibmf_i_init_cqs_err,
                    IBMF_TNF_ERROR, "", "%s, status = %d\n", tnf_string, msg,
                    "ibt_alloc_cq failed", tnf_uint, ibt_status, status);
                IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_init_cqs_end,
                    IBMF_TNF_TRACE, "", "ibmf_i_init_cqs() exit\n");
                return (IBMF_TRANSPORT_FAILURE);
        }
        ibt_set_cq_handler(cq_handle, ibmf_statep->ibmf_cq_handler, cip);
        cip->ci_alt_cq_handle = cq_handle;

        /* set state to CQ INITED */
        mutex_enter(&cip->ci_mutex);
        cip->ci_init_state |= IBMF_CI_INIT_CQ_INITED;
        mutex_exit(&cip->ci_mutex);

        IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_init_cqs_end,
            IBMF_TNF_TRACE, "", "ibmf_i_init_cqs() exit\n");

        return (IBMF_SUCCESS);
}

/*
 * ibmf_i_fini_cqs():
 *      Free up the completion queue
 */
static void
ibmf_i_fini_cqs(ibmf_ci_t *cip)
{
        ibt_status_t    status;
        uint_t          ci_init_state;

        IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_fini_cqs_start,
            IBMF_TNF_TRACE, "", "ibmf_i_fini_cqs() enter, cip = %p\n",
            tnf_opaque, cip, cip);

        mutex_enter(&cip->ci_mutex);
        ci_init_state = cip->ci_init_state;
        cip->ci_init_state &= ~IBMF_CI_INIT_CQ_INITED;
        mutex_exit(&cip->ci_mutex);

        if (ci_init_state & IBMF_CI_INIT_CQ_INITED) {
                status = ibt_free_cq(cip->ci_alt_cq_handle);
                if (status != IBT_SUCCESS) {
                        IBMF_TRACE_2(IBMF_TNF_NODEBUG, DPRINT_L3,
                            ibmf_i_fini_cqs_err, IBMF_TNF_ERROR, "",
                            "%s, status = %d\n", tnf_string, msg,
                            "ibt free cqs failed", tnf_uint, status, status);
                }

                status = ibt_free_cq(cip->ci_cq_handle);
                if (status != IBT_SUCCESS) {
                        IBMF_TRACE_2(IBMF_TNF_NODEBUG, DPRINT_L3,
                            ibmf_i_fini_cqs_err, IBMF_TNF_ERROR, "",
                            "%s, status = %d\n", tnf_string, msg,
                            "ibt free cqs failed", tnf_uint, status, status);
                }
        }

        IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_fini_cqs_end,
            IBMF_TNF_TRACE, "", "ibmf_i_fini_cqs() exit");
}

/*
 * ibmf_i_init_qplist():
 *      Set the QP list inited state flag
 */
static void
ibmf_i_init_qplist(ibmf_ci_t *ibmf_cip)
{
        IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_init_qplist_start,
            IBMF_TNF_TRACE, "", "ibmf_i_init_qplist() enter, cip = %p\n",
            tnf_opaque, cip, ibmf_cip);

        mutex_enter(&ibmf_cip->ci_mutex);
        ASSERT((ibmf_cip->ci_init_state & IBMF_CI_INIT_QP_LIST_INITED) == 0);
        ASSERT(ibmf_cip->ci_qp_list == NULL && ibmf_cip->ci_qp_list_tail ==
            NULL);
        cv_init(&ibmf_cip->ci_qp_cv, NULL, CV_DRIVER, NULL);
        ibmf_cip->ci_init_state |= IBMF_CI_INIT_QP_LIST_INITED;
        mutex_exit(&ibmf_cip->ci_mutex);

        IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_init_qplist_end,
            IBMF_TNF_TRACE, "", "ibmf_i_init_qplist() exit\n");
}

/*
 * ibmf_i_fini_qplist():
 *      Clean up the QP list
 */
static void
ibmf_i_fini_qplist(ibmf_ci_t *ibmf_cip)
{
        ibmf_qp_t *qpp;
        ibmf_alt_qp_t *altqpp;
        ibt_status_t status;

        IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_fini_qplist_start,
            IBMF_TNF_TRACE, "", "ibmf_i_fini_qplist() enter, cip = %p\n",
            tnf_opaque, cip, ibmf_cip);

        mutex_enter(&ibmf_cip->ci_mutex);

        if ((ibmf_cip->ci_init_state & IBMF_CI_INIT_QP_LIST_INITED) != 0) {

                /* walk through the qp list and free the memory */
                qpp = ibmf_cip->ci_qp_list;
                while (qpp != NULL) {
                        /* Remove qpp from the list */
                        ibmf_cip->ci_qp_list = qpp->iq_next;

                        ASSERT(qpp->iq_qp_ref == 0);
                        ASSERT(qpp->iq_flags == IBMF_QP_FLAGS_INVALID);
                        mutex_exit(&ibmf_cip->ci_mutex);
                        if (qpp->iq_qp_handle != NULL) {
                                /* Flush the special QP */
                                status = ibt_flush_qp(qpp->iq_qp_handle);
                                if (status != IBT_SUCCESS) {
                                        IBMF_TRACE_2(IBMF_TNF_NODEBUG,
                                            DPRINT_L1, ibmf_i_fini_qplist_err,
                                            IBMF_TNF_ERROR, "",
                                            "%s, status = %d\n", tnf_string,
                                            msg, "ibt_flush_qp returned error",
                                            tnf_int, status, status);
                                }

                                /* Grab the ci_mutex mutex before waiting */
                                mutex_enter(&ibmf_cip->ci_mutex);

                                /* Wait if WQEs for special QPs are alloced */
                                while (ibmf_cip->ci_wqes_alloced != 0) {
                                        cv_wait(&ibmf_cip->ci_wqes_cv,
                                            &ibmf_cip->ci_mutex);
                                }

                                mutex_exit(&ibmf_cip->ci_mutex);

                                /* Free the special QP */
                                status = ibt_free_qp(qpp->iq_qp_handle);
                                if (status != IBT_SUCCESS) {
                                        IBMF_TRACE_2(IBMF_TNF_NODEBUG,
                                            DPRINT_L1, ibmf_i_fini_qplist_err,
                                            IBMF_TNF_ERROR, "",
                                            "%s, status = %d\n", tnf_string,
                                            msg, "ibt_free_qp returned error",
                                            tnf_int, status, status);
                                }
                        }
                        mutex_destroy(&qpp->iq_mutex);
                        kmem_free((void *)qpp, sizeof (ibmf_qp_t));

                        /* Grab the mutex again before accessing the QP list */
                        mutex_enter(&ibmf_cip->ci_mutex);
                        qpp = ibmf_cip->ci_qp_list;
                }

                cv_destroy(&ibmf_cip->ci_qp_cv);

                ibmf_cip->ci_qp_list = ibmf_cip->ci_qp_list_tail = NULL;
                ibmf_cip->ci_init_state &=  ~IBMF_CI_INIT_QP_LIST_INITED;

                altqpp = ibmf_cip->ci_alt_qp_list;
                while (altqpp != NULL) {
                        /* Remove altqpp from the list */
                        ibmf_cip->ci_alt_qp_list = altqpp->isq_next;
                        mutex_exit(&ibmf_cip->ci_mutex);

                        if (altqpp->isq_qp_handle != NULL) {
                                /* Flush the special QP */
                                status = ibt_flush_qp(altqpp->isq_qp_handle);
                                if (status != IBT_SUCCESS) {
                                        IBMF_TRACE_2(IBMF_TNF_NODEBUG,
                                            DPRINT_L1, ibmf_i_fini_qplist_err,
                                            IBMF_TNF_ERROR, "",
                                            "%s, status = %d\n", tnf_string,
                                            msg, "ibt_flush_qp returned error",
                                            tnf_int, status, status);
                                }

                                /* Free the special QP */
                                status = ibt_free_qp(altqpp->isq_qp_handle);
                                if (status != IBT_SUCCESS) {
                                        IBMF_TRACE_2(IBMF_TNF_NODEBUG,
                                            DPRINT_L1, ibmf_i_fini_qplist_err,
                                            IBMF_TNF_ERROR, "",
                                            "%s, status = %d\n", tnf_string,
                                            msg, "ibt_free_qp returned error",
                                            tnf_int, status, status);
                                }
                        }
                        mutex_destroy(&altqpp->isq_mutex);
                        kmem_free((void *)altqpp, sizeof (ibmf_alt_qp_t));

                        /* Grab the mutex again before accessing the QP list */
                        mutex_enter(&ibmf_cip->ci_mutex);
                        altqpp = ibmf_cip->ci_alt_qp_list;
                }
        }

        mutex_exit(&ibmf_cip->ci_mutex);

        IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_fini_qplist_end,
            IBMF_TNF_TRACE, "", "ibmf_i_fini_qplist() exit\n");
}

/*
 * ibmf_i_alloc_client():
 *      Allocate and initialize the client structure.
 */
int
ibmf_i_alloc_client(ibmf_register_info_t *client_infop, uint_t flags,
    ibmf_client_t **clientpp)
{
        ibmf_client_t           *ibmf_clientp;
        char                    buf[128];
        ibmf_kstat_t            *ksp;

        IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_alloc_client_start,
            IBMF_TNF_TRACE, "", "ibmf_i_alloc_client() enter, "
            "client_infop = %p\n", tnf_opaque, client_infop, client_infop);

        _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*ibmf_clientp))

        /* allocate memory for ibmf_client and initialize it */
        ibmf_clientp = kmem_zalloc(sizeof (ibmf_client_t), KM_SLEEP);
        mutex_init(&ibmf_clientp->ic_mutex, NULL, MUTEX_DRIVER, NULL);
        mutex_init(&ibmf_clientp->ic_msg_mutex, NULL, MUTEX_DRIVER, NULL);
        mutex_init(&ibmf_clientp->ic_kstat_mutex, NULL, MUTEX_DRIVER, NULL);
        cv_init(&ibmf_clientp->ic_recv_cb_teardown_cv, NULL, CV_DRIVER, NULL);

        (void) sprintf(buf, "s%08X_0x%08X",
            (uint32_t)client_infop->ir_ci_guid, client_infop->ir_client_class);

        /* create a taskq to handle send completions based on reg flags */
        if ((flags & IBMF_REG_FLAG_NO_OFFLOAD) == 0) {
                if (flags & IBMF_REG_FLAG_SINGLE_OFFLOAD)
                        ibmf_clientp->ic_send_taskq = taskq_create(buf,
                            IBMF_TASKQ_1THREAD, MINCLSYSPRI, 1,
                            ibmf_taskq_max_tasks, TASKQ_PREPOPULATE);
                else
                        ibmf_clientp->ic_send_taskq = taskq_create(buf,
                            IBMF_TASKQ_NTHREADS, MINCLSYSPRI, 1,
                            ibmf_taskq_max_tasks,
                            TASKQ_DYNAMIC | TASKQ_PREPOPULATE);
                if (ibmf_clientp->ic_send_taskq == NULL) {
                        cv_destroy(&ibmf_clientp->ic_recv_cb_teardown_cv);
                        mutex_destroy(&ibmf_clientp->ic_mutex);
                        mutex_destroy(&ibmf_clientp->ic_msg_mutex);
                        mutex_destroy(&ibmf_clientp->ic_kstat_mutex);
                        kmem_free((void *)ibmf_clientp, sizeof (ibmf_client_t));
                        IBMF_TRACE_1(IBMF_TNF_NODEBUG, DPRINT_L1,
                            ibmf_i_alloc_client_err, IBMF_TNF_ERROR, "", "%s\n",
                            tnf_string, msg, buf);
                        IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
                            ibmf_i_alloc_client_end, IBMF_TNF_TRACE, "",
                            "ibmf_i_alloc_client() exit\n");
                        return (IBMF_NO_RESOURCES);
                }
        }
        ibmf_clientp->ic_init_state_class |= IBMF_CI_INIT_SEND_TASKQ_DONE;

        (void) sprintf(buf, "r%08X_0x%08X",
            (uint32_t)client_infop->ir_ci_guid, client_infop->ir_client_class);

        /* create a taskq to handle receive completions on reg flags */
        if ((flags & IBMF_REG_FLAG_NO_OFFLOAD) == 0) {
                if (flags & IBMF_REG_FLAG_SINGLE_OFFLOAD)
                        ibmf_clientp->ic_recv_taskq = taskq_create(buf,
                            IBMF_TASKQ_1THREAD, MINCLSYSPRI, 1,
                            ibmf_taskq_max_tasks, TASKQ_PREPOPULATE);
                else
                        ibmf_clientp->ic_recv_taskq = taskq_create(buf,
                            IBMF_TASKQ_NTHREADS, MINCLSYSPRI, 1,
                            ibmf_taskq_max_tasks,
                            TASKQ_DYNAMIC | TASKQ_PREPOPULATE);
                if (ibmf_clientp->ic_recv_taskq == NULL) {
                        cv_destroy(&ibmf_clientp->ic_recv_cb_teardown_cv);
                        mutex_destroy(&ibmf_clientp->ic_mutex);
                        mutex_destroy(&ibmf_clientp->ic_msg_mutex);
                        mutex_destroy(&ibmf_clientp->ic_kstat_mutex);
                        taskq_destroy(ibmf_clientp->ic_send_taskq);
                        kmem_free((void *)ibmf_clientp, sizeof (ibmf_client_t));
                        IBMF_TRACE_1(IBMF_TNF_NODEBUG, DPRINT_L1,
                            ibmf_i_alloc_client_err, IBMF_TNF_ERROR, "", "%s\n",
                            tnf_string, msg, buf);
                        IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
                            ibmf_i_alloc_client_end, IBMF_TNF_TRACE, "",
                            "ibmf_i_alloc_client() exit\n");
                        return (IBMF_NO_RESOURCES);
                }
        }
        ibmf_clientp->ic_init_state_class |= IBMF_CI_INIT_RECV_TASKQ_DONE;
        ibmf_clientp->ic_client_info.ci_guid = client_infop->ir_ci_guid;
        ibmf_clientp->ic_client_info.port_num = client_infop->ir_port_num;

        /* Get the base LID */
        (void) ibt_get_port_state_byguid(ibmf_clientp->ic_client_info.ci_guid,
            ibmf_clientp->ic_client_info.port_num, NULL,
            &ibmf_clientp->ic_base_lid);

        ibmf_clientp->ic_client_info.client_class =
            client_infop->ir_client_class;

        /* set up the per client ibmf kstats */
        (void) sprintf(buf, "ibmf_%016" PRIx64 "_%d_%X_stat",
            client_infop->ir_ci_guid, client_infop->ir_port_num,
            client_infop->ir_client_class);
        if ((ibmf_clientp->ic_kstatp = kstat_create("ibmf", 0, buf, "misc",
            KSTAT_TYPE_NAMED, sizeof (ibmf_kstat_t) / sizeof (kstat_named_t),
            KSTAT_FLAG_WRITABLE)) == NULL) {
                cv_destroy(&ibmf_clientp->ic_recv_cb_teardown_cv);
                mutex_destroy(&ibmf_clientp->ic_mutex);
                mutex_destroy(&ibmf_clientp->ic_msg_mutex);
                mutex_destroy(&ibmf_clientp->ic_kstat_mutex);
                if ((flags & IBMF_REG_FLAG_NO_OFFLOAD) == 0) {
                        taskq_destroy(ibmf_clientp->ic_send_taskq);
                        taskq_destroy(ibmf_clientp->ic_recv_taskq);
                }
                kmem_free((void *)ibmf_clientp, sizeof (ibmf_client_t));
                IBMF_TRACE_1(IBMF_TNF_NODEBUG, DPRINT_L1,
                    ibmf_i_alloc_client_err, IBMF_TNF_ERROR, "", "%s\n",
                    tnf_string, msg, "kstat creation failed");
                IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
                    ibmf_i_alloc_client_end, IBMF_TNF_TRACE, "",
                    "ibmf_i_alloc_client() exit\n");
                return (IBMF_NO_RESOURCES);
        }
        ksp = (ibmf_kstat_t *)ibmf_clientp->ic_kstatp->ks_data;
        kstat_named_init(&ksp->msgs_alloced, "messages_allocated",
            KSTAT_DATA_UINT32);
        kstat_named_init(&ksp->msgs_active, "messages_active",
            KSTAT_DATA_UINT32);
        kstat_named_init(&ksp->msgs_sent, "messages_sent", KSTAT_DATA_UINT32);
        kstat_named_init(&ksp->msgs_received, "messages_received",
            KSTAT_DATA_UINT32);
        kstat_named_init(&ksp->sends_active, "sends_active", KSTAT_DATA_UINT32);
        kstat_named_init(&ksp->recvs_active, "receives_active",
            KSTAT_DATA_UINT32);
        kstat_named_init(&ksp->ud_dests_alloced, "ud_dests_allocated",
            KSTAT_DATA_UINT32);
        kstat_named_init(&ksp->alt_qps_alloced, "alt_qps_allocated",
            KSTAT_DATA_UINT32);
        kstat_named_init(&ksp->send_cb_active, "send_callbacks_active",
            KSTAT_DATA_UINT32);
        kstat_named_init(&ksp->recv_cb_active, "receive_callbacks_active",
            KSTAT_DATA_UINT32);
        kstat_named_init(&ksp->recv_bufs_alloced, "receive_bufs_allocated",
            KSTAT_DATA_UINT32);
        kstat_named_init(&ksp->msg_allocs_failed, "msg_allocs_failed",
            KSTAT_DATA_UINT32);
        kstat_named_init(&ksp->uddest_allocs_failed, "uddest_allocs_failed",
            KSTAT_DATA_UINT32);
        kstat_named_init(&ksp->alt_qp_allocs_failed, "alt_qp_allocs_failed",
            KSTAT_DATA_UINT32);
        kstat_named_init(&ksp->send_pkt_failed, "send_pkt_failed",
            KSTAT_DATA_UINT32);
        kstat_named_init(&ksp->rmpp_errors, "rmpp_errors",
            KSTAT_DATA_UINT32);

        kstat_install(ibmf_clientp->ic_kstatp);

        *clientpp = ibmf_clientp;

        _NOTE(NOW_VISIBLE_TO_OTHER_THREADS(*ibmf_clientp))

        IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_alloc_client_end,
            IBMF_TNF_TRACE, "", "ibmf_i_alloc_client() exit\n");

        return (IBMF_SUCCESS);
}

/*
 * ibmf_i_free_client():
 *      Free up the client structure and release resources
 */
void
ibmf_i_free_client(ibmf_client_t *clientp)
{
        IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_free_client_start,
            IBMF_TNF_TRACE, "", "ibmf_i_free_client() enter, clientp = %p\n",
            tnf_opaque, clientp, clientp);

        /* delete the general ibmf kstats */
        if (clientp->ic_kstatp != NULL) {
                kstat_delete(clientp->ic_kstatp);
                clientp->ic_kstatp = NULL;
        }

        /* release references and destroy the resources */
        if (clientp->ic_init_state_class & IBMF_CI_INIT_SEND_TASKQ_DONE) {
                if ((clientp->ic_reg_flags & IBMF_REG_FLAG_NO_OFFLOAD) == 0) {
                        taskq_destroy(clientp->ic_send_taskq);
                }
                clientp->ic_init_state_class &= ~IBMF_CI_INIT_SEND_TASKQ_DONE;
        }

        if (clientp->ic_init_state_class & IBMF_CI_INIT_RECV_TASKQ_DONE) {
                if ((clientp->ic_reg_flags & IBMF_REG_FLAG_NO_OFFLOAD) == 0) {
                        taskq_destroy(clientp->ic_recv_taskq);
                }
                clientp->ic_init_state_class &= ~IBMF_CI_INIT_RECV_TASKQ_DONE;
        }

        mutex_destroy(&clientp->ic_mutex);
        mutex_destroy(&clientp->ic_msg_mutex);
        mutex_destroy(&clientp->ic_kstat_mutex);
        cv_destroy(&clientp->ic_recv_cb_teardown_cv);
        kmem_free((void *)clientp, sizeof (ibmf_client_t));

        IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_free_client_end,
            IBMF_TNF_TRACE, "", "ibmf_i_free_client() exit\n");
}

/*
 * ibmf_i_validate_classes_and_port():
 *      Validate the class type and get the client structure
 */
int
ibmf_i_validate_classes_and_port(ibmf_ci_t *ibmf_cip,
    ibmf_register_info_t *client_infop)
{
        ibmf_client_t           *ibmf_clientp;
        int                     status;

        IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L4,
            ibmf_i_validate_classes_and_port_start, IBMF_TNF_TRACE, "",
            "ibmf_i_validate_classes_and_port() enter, cip = %p, "
            "clientp = %p\n", tnf_opaque, cip, ibmf_cip,
            tnf_opaque, client_infop, client_infop);

        /*
         * the Solaris implementation of IBMF does not support
         * the UNIVERSAL_CLASS
         */
        if (client_infop->ir_client_class == UNIVERSAL_CLASS) {
                IBMF_TRACE_1(IBMF_TNF_NODEBUG, DPRINT_L1,
                    ibmf_i_validate_classes_and_port_err, IBMF_TNF_ERROR, "",
                    "%s\n", tnf_string, msg,
                    "UNIVERSAL class is not supported");
                IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
                    ibmf_i_validate_classes_and_port_end, IBMF_TNF_TRACE, "",
                    "ibmf_i_validate_classes_and_port() exit\n");
                return (IBMF_NOT_SUPPORTED);
        }

        /*
         * Check if the client context already exists on the list
         * maintained in the CI context. If it is, then the client class
         * has already been registered for.
         */
        status = ibmf_i_lookup_client_by_info(ibmf_cip, client_infop,
            &ibmf_clientp);
        if (status != IBMF_SUCCESS) {
                /* client class has not been previously registered for */
                status = IBMF_SUCCESS;
        } else {
                IBMF_TRACE_1(IBMF_TNF_NODEBUG, DPRINT_L1,
                    ibmf_i_validate_classes_and_port_err, IBMF_TNF_ERROR, "",
                    "client already registered, class = 0x%X\n",
                    tnf_uint, class, client_infop->ir_client_class);
                status = IBMF_PORT_IN_USE;
        }

        IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
            ibmf_i_validate_classes_and_port_end, IBMF_TNF_TRACE, "",
            "ibmf_i_validate_classes_and_port() exit\n");
        return (status);
}

/*
 * ibmf_i_lookup_client_by_info():
 *      Get the client structure from the list
 */
static int
ibmf_i_lookup_client_by_info(ibmf_ci_t *ibmf_cip,
    ibmf_register_info_t *ir_client, ibmf_client_t **clientpp)
{
        ibmf_client_t *clientp;

        IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L4,
            ibmf_i_lookup_client_by_info_start, IBMF_TNF_TRACE, "",
            "ibmf_i_lookup_client_by_info() enter, cip = %p, clientinfo = %p\n",
            tnf_opaque, cip, ibmf_cip, tnf_opaque, clientinfo, ir_client);

        ASSERT(MUTEX_NOT_HELD(&ibmf_cip->ci_clients_mutex));

        /*
         * walk the CI's client list searching for one with the specified class
         */
        mutex_enter(&ibmf_cip->ci_clients_mutex);
        clientp = ibmf_cip->ci_clients;
        while (clientp != NULL) {
                ibmf_client_info_t *tmp = &clientp->ic_client_info;
                if (tmp->client_class == ir_client->ir_client_class &&
                    ir_client->ir_client_class != UNIVERSAL_CLASS &&
                    tmp->ci_guid == ir_client->ir_ci_guid &&
                    tmp->port_num == ir_client->ir_port_num) {
                        /* found our match */
                        break;
                }
                clientp = clientp->ic_next;
        }
        mutex_exit(&ibmf_cip->ci_clients_mutex);

        if (clientp != NULL) {
                *clientpp = clientp;
                IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L4,
                    ibmf_i_lookup_client_by_info_end, IBMF_TNF_TRACE, "",
                    "ibmf_i_lookup_client_by_info(): clientp = %p\n",
                    tnf_opaque, clientp, clientp);
                return (IBMF_SUCCESS);
        } else {
                IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
                    ibmf_i_lookup_client_by_info_end, IBMF_TNF_TRACE, "",
                    "ibmf_i_lookup_client_by_info() exit\n");
                return (IBMF_FAILURE);
        }
}

/*
 * ibmf_i_add_client():
 *      Add a new client to the client list
 */
void
ibmf_i_add_client(ibmf_ci_t *ibmf_cip, ibmf_client_t *ibmf_clientp)
{
        IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_add_start,
            IBMF_TNF_TRACE, "",
            "ibmf_i_add_client() enter, cip = %p, clientp = %p\n",
            tnf_opaque, ibmf_ci, ibmf_cip, tnf_opaque, client, ibmf_clientp);

        ASSERT(MUTEX_NOT_HELD(&ibmf_cip->ci_clients_mutex));

        mutex_enter(&ibmf_cip->ci_clients_mutex);
        ibmf_clientp->ic_next = NULL;
        ibmf_clientp->ic_prev = ibmf_cip->ci_clients_last;
        if (ibmf_cip->ci_clients == NULL) {
                ibmf_cip->ci_clients = ibmf_clientp;
        }
        if (ibmf_cip->ci_clients_last) {
                ibmf_cip->ci_clients_last->ic_next = ibmf_clientp;
        }
        ibmf_cip->ci_clients_last = ibmf_clientp;
        mutex_exit(&ibmf_cip->ci_clients_mutex);

        IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_add_end,
            IBMF_TNF_TRACE, "", "ibmf_i_add_client() exit\n");
}

/*
 * ibmf_i_delete_client():
 *      Delete a client from the client list
 */
void
ibmf_i_delete_client(ibmf_ci_t *ibmf_cip, ibmf_client_t *ibmf_clientp)
{
        IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_delete_client_start,
            IBMF_TNF_TRACE, "", "ibmf_i_delete_client() enter, "
            "ibmf_i_delete_client() enter, cip = %p, clientp = %p\n",
            tnf_opaque, ibmf_ci, ibmf_cip, tnf_opaque, client, ibmf_clientp);

        ASSERT(MUTEX_NOT_HELD(&ibmf_cip->ci_clients_mutex));

        mutex_enter(&ibmf_cip->ci_clients_mutex);
        if (ibmf_clientp->ic_next)
                ibmf_clientp->ic_next->ic_prev = ibmf_clientp->ic_prev;

        if (ibmf_clientp->ic_prev)
                ibmf_clientp->ic_prev->ic_next = ibmf_clientp->ic_next;

        if (ibmf_cip->ci_clients == ibmf_clientp) {
                ibmf_cip->ci_clients = ibmf_clientp->ic_next;
        }
        if (ibmf_cip->ci_clients_last == ibmf_clientp) {
                ibmf_cip->ci_clients_last = ibmf_clientp->ic_prev;
        }
        mutex_exit(&ibmf_cip->ci_clients_mutex);

        IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_delete_client_end,
            IBMF_TNF_TRACE, "", "ibmf_i_delete_client() exit\n");
}

/*
 * ibmf_i_get_qp():
 *      Get the QP structure based on the client class
 */
int
ibmf_i_get_qp(ibmf_ci_t *ibmf_cip, uint_t port_num, ibmf_client_type_t class,
    ibmf_qp_t **qppp)
{
        ibmf_qp_t               *qpp;
        int                     qp_num, status = IBMF_SUCCESS;

        IBMF_TRACE_3(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_get_qp_start,
            IBMF_TNF_TRACE, "", "ibmf_i_get_qp() enter, cip = %p, "
            "port = %d, class = %x\n", tnf_opaque, ibmf_ci, ibmf_cip,
            tnf_int, port, port_num, tnf_opaque, class, class);

        ASSERT(MUTEX_NOT_HELD(&ibmf_cip->ci_mutex));

        mutex_enter(&ibmf_cip->ci_mutex);

        /*
         * walk through the list of qps on this ci, looking for one that
         * corresponds to the type and class the caller is interested in.
         * If it is not there, we need allocate it from the transport. Since
         * qp0 & qp1 can only be allocated once, we maintain a reference count
         * and call the transport for allocation iff the ref count is 0.
         */
        qp_num = (class == SUBN_AGENT || class == SUBN_MANAGER) ? 0 : 1;

        qpp = ibmf_cip->ci_qp_list;
        while (qpp != NULL) {
                if (port_num == qpp->iq_port_num && qp_num == qpp->iq_qp_num)
                        break;
                qpp = qpp->iq_next;
        }

        if (qpp == NULL) {
                /*
                 * allocate qp and add it the qp list; recheck to
                 * catch races
                 */
                ibmf_qp_t *tqpp;

                mutex_exit(&ibmf_cip->ci_mutex);

                tqpp = (ibmf_qp_t *)kmem_zalloc(sizeof (ibmf_qp_t), KM_SLEEP);

                /* check the list under lock */
                mutex_enter(&ibmf_cip->ci_mutex);

                qpp = ibmf_cip->ci_qp_list;
                while (qpp != NULL) {
                        if (port_num == qpp->iq_port_num && qp_num ==
                            qpp->iq_qp_num)
                                break;
                        qpp = qpp->iq_next;
                }

                if (qpp != NULL) {
                        /* some one raced past us and added to the list */
                        kmem_free((void *)tqpp, sizeof (ibmf_qp_t));
                } else {
                        /* add this to the qp list */
                        qpp = tqpp;
                        _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*qpp))
                        qpp->iq_next = NULL;
                        if (ibmf_cip->ci_qp_list == NULL)
                                ibmf_cip->ci_qp_list = qpp;
                        if (ibmf_cip->ci_qp_list_tail != NULL)
                                ibmf_cip->ci_qp_list_tail->iq_next = qpp;
                        ibmf_cip->ci_qp_list_tail = qpp;
                        qpp->iq_port_num = port_num;
                        qpp->iq_qp_num = qp_num;
                        qpp->iq_flags = IBMF_QP_FLAGS_INVALID;
                        mutex_init(&qpp->iq_mutex, NULL, MUTEX_DRIVER, NULL);
                }
        }

        /* we now have a QP context */
        for (;;) {
                if (qpp->iq_flags == IBMF_QP_FLAGS_INITING) {

                        /* block till qp is in VALID state */
                        cv_wait(&ibmf_cip->ci_qp_cv, &ibmf_cip->ci_mutex);
                        continue;

                }

                if (qpp->iq_flags == IBMF_QP_FLAGS_UNINITING) {

                        /* block till qp is in INVALID state */
                        cv_wait(&ibmf_cip->ci_qp_cv, &ibmf_cip->ci_mutex);
                        continue;
                }

                if (qpp->iq_flags == IBMF_QP_FLAGS_INVALID) {
                        if ((status = ibmf_i_init_qp(ibmf_cip, qpp)) !=
                            IBMF_SUCCESS) {
                                ibmf_qp_t *tqpp;

                                /*
                                 * Remove the QP context from the CI's list.
                                 * Only initialized QPs should be on the list.
                                 * We know that this QP is on the list, so
                                 * the list is not empty.
                                 */
                                tqpp = ibmf_cip->ci_qp_list;
                                if (tqpp == qpp) {
                                        /* Only QP context on the list */
                                        ibmf_cip->ci_qp_list = NULL;
                                        ibmf_cip->ci_qp_list_tail = NULL;
                                }

                                /* Find the QP context before the last one */
                                if (tqpp != qpp) {
                                        while (tqpp->iq_next != qpp) {
                                                tqpp = tqpp->iq_next;
                                        }

                                        /*
                                         * We are at the second last element of
                                         * the list. Readjust the tail pointer.
                                         * Remove the last element from the
                                         * list.
                                         */
                                        tqpp->iq_next = NULL;
                                        ibmf_cip->ci_qp_list_tail = tqpp;
                                }

                                /* Free up the QP context */
                                kmem_free((void *)qpp, sizeof (ibmf_qp_t));

                                break;
                        }
                        continue;
                }

                if (qpp->iq_flags == IBMF_QP_FLAGS_INITED) {
                        qpp->iq_qp_ref++;
                        break;
                }
        }

        _NOTE(NOW_VISIBLE_TO_OTHER_THREADS(*qpp))

        mutex_exit(&ibmf_cip->ci_mutex);

        if (status == IBMF_SUCCESS) {
                *qppp = qpp;
                IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_get_qp_end,
                    IBMF_TNF_TRACE, "", "ibmf_i_get_qp() exit "
                    "qp_handle = %p\n", tnf_opaque, qp_handle, qpp);
                return (IBMF_SUCCESS);
        } else {
                IBMF_TRACE_1(IBMF_TNF_NODEBUG, DPRINT_L1, ibmf_i_get_qp_err,
                    IBMF_TNF_ERROR, "", "%s\n", tnf_string, msg,
                    "ibmf_i_get_qp(): qp_not found");
                IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_get_qp_end,
                    IBMF_TNF_TRACE, "", "ibmf_i_get_qp() exit\n");
                return (status);
        }
}

/*
 * ibmf_i_release_qp():
 *      Drop the reference count on the QP structure
 */
void
ibmf_i_release_qp(ibmf_ci_t *ibmf_cip, ibmf_qp_t **qppp)
{
        ibmf_qp_t       *qpp;

        IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_release_qp_start,
            IBMF_TNF_TRACE, "", "ibmf_i_release_qp() enter, cip = %p, "
            "qpp = %p\n", tnf_opaque, cip, ibmf_cip, tnf_opaque, qpp, *qppp);

        ASSERT(MUTEX_NOT_HELD(&ibmf_cip->ci_mutex));

        mutex_enter(&ibmf_cip->ci_mutex);
        qpp = *qppp;
        qpp->iq_qp_ref--;
        if (qpp->iq_qp_ref == 0)
                ibmf_i_uninit_qp(ibmf_cip, qpp);
        mutex_exit(&ibmf_cip->ci_mutex);

        IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_release_qp_end,
            IBMF_TNF_TRACE, "", "ibmf_i_release_qp() exit\n");
}

/*
 * ibmf_i_init_qp():
 *      Set up the QP context, request a QP from the IBT framework
 *      and initialize it
 */
static int
ibmf_i_init_qp(ibmf_ci_t *ibmf_cip, ibmf_qp_t *qpp)
{
        ibt_sqp_type_t          qp_type;
        ibt_qp_alloc_attr_t     qp_attrs;
        ibt_qp_hdl_t            qp_handle;
        ibt_qp_info_t           qp_modify_attr;
        ibt_status_t            ibt_status;
        int                     i, status;

        IBMF_TRACE_3(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_init_qp_start,
            IBMF_TNF_TRACE, "", "ibmf_i_init_qp() enter, cip = %p, "
            "port = %d, qp = %d\n", tnf_opaque, ibmf_ci, ibmf_cip, tnf_int,
            port, qpp->iq_port_num, tnf_int, num, qpp->iq_qp_num);

        _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(qpp->iq_qp_handle))

        ASSERT(MUTEX_HELD(&ibmf_cip->ci_mutex));

        qpp->iq_flags = IBMF_QP_FLAGS_INITING;
        mutex_exit(&ibmf_cip->ci_mutex);
        if (qpp->iq_qp_handle) {        /* closed but not yet freed */
                ibt_status = ibt_free_qp(qpp->iq_qp_handle);
                if (ibt_status != IBT_SUCCESS) {
                        IBMF_TRACE_2(IBMF_TNF_NODEBUG, DPRINT_L1,
                            ibmf_i_init_qp_err, IBMF_TNF_ERROR, "",
                            "%s, status = %d\n", tnf_string, msg,
                            "ibt_free_qp returned error",
                            tnf_uint, ibt_status, ibt_status);
                }
                qpp->iq_qp_handle = NULL;
        }
        ASSERT(qpp->iq_qp_num == 0 || qpp->iq_qp_num == 1);
        if (qpp->iq_qp_num == 0)
                qp_type = IBT_SMI_SQP;
        else
                qp_type = IBT_GSI_SQP;
        qp_attrs.qp_scq_hdl = ibmf_cip->ci_cq_handle;
        qp_attrs.qp_rcq_hdl = ibmf_cip->ci_cq_handle;
        qp_attrs.qp_pd_hdl = ibmf_cip->ci_pd;
        qp_attrs.qp_sizes.cs_sq_sgl = 1;
        qp_attrs.qp_sizes.cs_rq_sgl = IBMF_MAX_RQ_WR_SGL_ELEMENTS;
        qp_attrs.qp_sizes.cs_sq = ibmf_send_wqes_posted_per_qp;
        qp_attrs.qp_sizes.cs_rq = ibmf_recv_wqes_posted_per_qp;
        qp_attrs.qp_flags = IBT_ALL_SIGNALED;
        qp_attrs.qp_alloc_flags = IBT_QP_NO_FLAGS;

        /* call the IB transport to allocate a special QP */
        ibt_status = ibt_alloc_special_qp(ibmf_cip->ci_ci_handle,
            qpp->iq_port_num, qp_type, &qp_attrs, NULL, &qp_handle);
        if (ibt_status != IBT_SUCCESS) {
                mutex_enter(&ibmf_cip->ci_mutex);
                qpp->iq_flags = IBMF_QP_FLAGS_INVALID;
                cv_broadcast(&ibmf_cip->ci_qp_cv);
                IBMF_TRACE_1(IBMF_TNF_NODEBUG, DPRINT_L1, ibmf_i_init_qp_err,
                    IBMF_TNF_ERROR, "", "ibmf_i_init_qp() error status = %d\n",
                    tnf_uint, ibt_status, ibt_status);
                IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_init_qp_end,
                    IBMF_TNF_TRACE, "", "ibmf_i_init_qp() exit\n");
                return (IBMF_TRANSPORT_FAILURE);
        }

        /* initialize qpp */
        qpp->iq_qp_handle = qp_handle;
        qp_modify_attr.qp_trans = IBT_UD_SRV;
        qp_modify_attr.qp_flags = IBT_CEP_NO_FLAGS;

        /* get the pkey index for the specified pkey */
        if (ibmf_i_get_pkeyix(ibmf_cip->ci_ci_handle, IBMF_P_KEY_DEF_LIMITED,
            qpp->iq_port_num, &qp_modify_attr.qp_transport.ud.ud_pkey_ix) !=
            IBMF_SUCCESS) {
                ibt_status = ibt_free_qp(qpp->iq_qp_handle);
                if (ibt_status != IBT_SUCCESS) {
                        IBMF_TRACE_2(IBMF_TNF_NODEBUG, DPRINT_L1,
                            ibmf_i_init_qp_err, IBMF_TNF_ERROR, "",
                            "%s, status = %d\n", tnf_string, msg,
                            "ibt_free_qp returned error",
                            tnf_uint, ibt_status, ibt_status);
                }
                mutex_enter(&ibmf_cip->ci_mutex);
                qpp->iq_flags = IBMF_QP_FLAGS_INVALID;
                cv_broadcast(&ibmf_cip->ci_qp_cv);
                IBMF_TRACE_0(IBMF_TNF_NODEBUG, DPRINT_L1, ibmf_i_init_qp_err,
                    IBMF_TNF_ERROR, "", "ibmf_init_qp(): failed to get "
                    "pkey index\n");
                IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_init_qp_end,
                    IBMF_TNF_TRACE, "", "ibmf_i_init_qp() exit\n");
                return (IBMF_FAILURE);
        }
        qp_modify_attr.qp_transport.ud.ud_sq_psn = 0;
        qp_modify_attr.qp_transport.ud.ud_port = qpp->iq_port_num;
        qp_modify_attr.qp_transport.ud.ud_qkey = IBMF_MGMT_Q_KEY;

        /* call the IB transport to initialize the QP */
        ibt_status = ibt_initialize_qp(qp_handle, &qp_modify_attr);
        if (ibt_status != IBT_SUCCESS) {
                ibt_status = ibt_free_qp(qpp->iq_qp_handle);
                if (ibt_status != IBT_SUCCESS) {
                        IBMF_TRACE_2(IBMF_TNF_NODEBUG, DPRINT_L1,
                            ibmf_i_init_qp_err, IBMF_TNF_ERROR, "",
                            "%s, status = %d\n", tnf_string, msg,
                            "ibt_free_qp returned error",
                            tnf_uint, ibt_status, ibt_status);
                }
                mutex_enter(&ibmf_cip->ci_mutex);
                qpp->iq_flags = IBMF_QP_FLAGS_INVALID;
                cv_broadcast(&ibmf_cip->ci_qp_cv);
                IBMF_TRACE_1(IBMF_TNF_NODEBUG, DPRINT_L1, ibmf_i_init_qp_err,
                    IBMF_TNF_ERROR, "", "ibmf_init_qp(): error status = %d\n",
                    tnf_uint, ibt_status, ibt_status);
                IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_init_qp_end,
                    IBMF_TNF_TRACE, "", "ibmf_i_init_qp() exit\n");
                return (IBMF_TRANSPORT_FAILURE);
        }

        /* post receive wqes to the RQ to handle unsolicited inbound packets  */
        for (i = 0; i < ibmf_recv_wqes_per_port; i++) {
                status =  ibmf_i_post_recv_buffer(ibmf_cip, qpp,
                    B_TRUE, IBMF_QP_HANDLE_DEFAULT);
                if (status != IBMF_SUCCESS) {
                        IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L1,
                            ibmf_i_init_qp, IBMF_TNF_TRACE, "",
                            "%s\n", tnf_string, msg, "ibmf_i_init_qp(): "
                            "ibmf_i_post_recv_buffer() failed");
                }
        }
        mutex_enter(&ibmf_cip->ci_mutex);

        /* set the state and signal blockers */
        qpp->iq_flags = IBMF_QP_FLAGS_INITED;
        cv_broadcast(&ibmf_cip->ci_qp_cv);

        IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_init_qp_end,
            IBMF_TNF_TRACE, "", "ibmf_i_init_qp() exit\n");
        return (IBMF_SUCCESS);
}

/*
 * ibmf_i_uninit_qp():
 *      Invalidate the QP context
 */
static void
ibmf_i_uninit_qp(ibmf_ci_t *ibmf_cip, ibmf_qp_t *qpp)
{
        ibt_status_t            status;

        IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_uninit_qp_start,
            IBMF_TNF_TRACE, "", "ibmf_i_uninit_qp() enter, cip = %p "
            "qpp = %p\n", tnf_opaque, cip, ibmf_cip, tnf_opaque, qpp, qpp);

        ASSERT(MUTEX_HELD(&ibmf_cip->ci_mutex));

        /* mark the state as uniniting */
        ASSERT(qpp->iq_qp_ref == 0);
        qpp->iq_flags = IBMF_QP_FLAGS_UNINITING;
        mutex_exit(&ibmf_cip->ci_mutex);

        /* note: we ignore error values from ibt_flush_qp */
        status = ibt_flush_qp(qpp->iq_qp_handle);
        if (status != IBT_SUCCESS) {
                IBMF_TRACE_2(IBMF_TNF_NODEBUG, DPRINT_L2,
                    ibmf_i_uninit_qp_err, IBMF_TNF_ERROR, "",
                    "ibmf_i_uninit_qp(): %s, status = %d\n", tnf_string, msg,
                    "ibt_flush_qp returned error", tnf_int, status, status);
        }

        /* mark state as INVALID and signal any blockers */
        mutex_enter(&ibmf_cip->ci_mutex);
        qpp->iq_flags = IBMF_QP_FLAGS_INVALID;
        cv_broadcast(&ibmf_cip->ci_qp_cv);

        IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_uninit_qp_end,
            IBMF_TNF_TRACE, "", "ibmf_i_uninit_qp() exit\n");
}

/*
 * ibmf_i_alloc_msg():
 *      Allocate and set up a message context
 */
int
ibmf_i_alloc_msg(ibmf_client_t *clientp, ibmf_msg_impl_t **msgp, int km_flags)
{
        ibmf_msg_impl_t *msgimplp;

        IBMF_TRACE_3(IBMF_TNF_DEBUG, DPRINT_L4,
            ibmf_i_alloc_msg_start, IBMF_TNF_TRACE, "",
            "ibmf_i_alloc_msg() enter, clientp = %p, msg = %p, "
            " kmflags = %d\n", tnf_opaque, clientp, clientp, tnf_opaque, msg,
            *msgp, tnf_int, km_flags, km_flags);

        /* allocate the message context */
        msgimplp = (ibmf_msg_impl_t *)kmem_zalloc(sizeof (ibmf_msg_impl_t),
            km_flags);
        if (msgimplp != NULL) {
                if (km_flags == KM_SLEEP) {
                        ibmf_i_pop_ud_dest_thread(clientp->ic_myci);
                }
        } else {
                IBMF_TRACE_1(IBMF_TNF_NODEBUG, DPRINT_L1,
                    ibmf_i_alloc_msg_err, IBMF_TNF_ERROR, "",
                    "ibmf_i_alloc_msg(): %s\n",
                    tnf_string, msg, "kmem_xalloc failed");
                IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_alloc_msg_end,
                    IBMF_TNF_TRACE, "", "ibmf_i_alloc_msg() exit\n");
                return (IBMF_NO_RESOURCES);
        }

        *msgp = msgimplp;
        IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_alloc_msg_end,
            IBMF_TNF_TRACE, "", "ibmf_i_alloc_msg() exit\n");
        return (IBMF_SUCCESS);
}

/*
 * ibmf_i_free_msg():
 *      frees up all buffers allocated by IBMF for
 *      this message context, and then frees up the context
 */
void
ibmf_i_free_msg(ibmf_msg_impl_t *msgimplp)
{
        ibmf_msg_bufs_t *msgbufp = &msgimplp->im_msgbufs_recv;
        ibmf_client_t *clientp = (ibmf_client_t *)msgimplp->im_client;
        uint32_t        cl_hdr_sz, cl_hdr_off;

        IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L4,
            ibmf_i_free_msg_start, IBMF_TNF_TRACE, "",
            "ibmf_i_free_msg() enter, msg = %p\n", tnf_opaque, msg, msgimplp);

        /* free up the UD destination resource */
        if (msgimplp->im_ibmf_ud_dest != NULL) {
                ibmf_i_free_ud_dest(clientp, msgimplp);
                ibmf_i_clean_ud_dest_list(clientp->ic_myci, B_FALSE);
        }

        /* free up the receive buffer if allocated previously */
        if (msgbufp->im_bufs_mad_hdr != NULL) {
                ibmf_i_mgt_class_to_hdr_sz_off(
                    msgbufp->im_bufs_mad_hdr->MgmtClass,
                    &cl_hdr_sz, &cl_hdr_off);
                kmem_free(msgbufp->im_bufs_mad_hdr, sizeof (ib_mad_hdr_t) +
                    cl_hdr_off + msgbufp->im_bufs_cl_hdr_len +
                    msgbufp->im_bufs_cl_data_len);
                mutex_enter(&clientp->ic_kstat_mutex);
                IBMF_SUB32_KSTATS(clientp, recv_bufs_alloced, 1);
                mutex_exit(&clientp->ic_kstat_mutex);
        }

        /* destroy the message mutex */
        mutex_destroy(&msgimplp->im_mutex);

        /* free the message context */
        kmem_free(msgimplp, sizeof (ibmf_msg_impl_t));

        IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_free_msg_end,
            IBMF_TNF_TRACE, "", "ibmf_i_free_msg() exit\n");
}

/*
 * ibmf_i_msg_transport():
 *      Send a message posted by the IBMF client using the RMPP protocol
 *      if specified
 */
int
ibmf_i_msg_transport(ibmf_client_t *clientp, ibmf_qp_handle_t ibmf_qp_handle,
    ibmf_msg_impl_t *msgimplp, int blocking)
{
        ib_mad_hdr_t    *madhdrp;
        ibmf_msg_bufs_t *msgbufp, *smsgbufp;
        uint32_t        cl_hdr_sz, cl_hdr_off;
        boolean_t       isDS = 0; /* double sided (sequenced) transaction */
        boolean_t       error = B_FALSE;
        int             status = IBMF_SUCCESS;
        uint_t          refcnt;
        char            errmsg[128];
        timeout_id_t    msg_rp_unset_id, msg_tr_unset_id;

        IBMF_TRACE_4(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_msg_transport_start,
            IBMF_TNF_TRACE, "", "ibmf_i_msg_transport(): clientp = 0x%p, "
            "qphdl = 0x%p, msgp = 0x%p, block = %d\n",
            tnf_opaque, clientp, clientp, tnf_opaque, qphdl, ibmf_qp_handle,
            tnf_opaque, msg, msgimplp, tnf_uint, block, blocking);

        _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*msgimplp, *msgbufp))

        mutex_enter(&msgimplp->im_mutex);

        madhdrp = msgimplp->im_msgbufs_send.im_bufs_mad_hdr;
        msgbufp = &msgimplp->im_msgbufs_recv;
        smsgbufp = &msgimplp->im_msgbufs_send;

        /*
         * check if transp_op_flags specify that the transaction is
         * a single packet, then the size of the message header + data
         * does not exceed 256 bytes
         */
        if ((msgimplp->im_transp_op_flags & IBMF_MSG_TRANS_FLAG_RMPP) == 0) {
                ibmf_i_mgt_class_to_hdr_sz_off(
                    smsgbufp->im_bufs_mad_hdr->MgmtClass,
                    &cl_hdr_sz, &cl_hdr_off);

                if ((sizeof (ib_mad_hdr_t) + cl_hdr_off +
                    smsgbufp->im_bufs_cl_hdr_len +
                    smsgbufp->im_bufs_cl_data_len) > IBMF_MAD_SIZE) {
                        mutex_exit(&msgimplp->im_mutex);
                        (void) sprintf(errmsg,
                            "Non-RMPP message size is too large");
                        error = B_TRUE;
                        status = IBMF_BAD_SIZE;
                        goto bail;
                }
        }

        /* more message context initialization */
        msgimplp->im_qp_hdl     = ibmf_qp_handle;
        msgimplp->im_tid        = b2h64(madhdrp->TransactionID);
        msgimplp->im_mgt_class  = madhdrp->MgmtClass;
        msgimplp->im_unsolicited = B_FALSE;
        msgimplp->im_trans_state_flags = IBMF_TRANS_STATE_FLAG_UNINIT;
        bzero(&msgimplp->im_rmpp_ctx, sizeof (ibmf_rmpp_ctx_t));
        msgimplp->im_rmpp_ctx.rmpp_state = IBMF_RMPP_STATE_UNDEFINED;
        msgimplp->im_rmpp_ctx.rmpp_respt = IBMF_RMPP_DEFAULT_RRESPT;
        msgimplp->im_rmpp_ctx.rmpp_retry_cnt = 0;
        msgimplp->im_ref_count = 0;
        msgimplp->im_pending_send_compls = 0;
        IBMF_MSG_INCR_REFCNT(msgimplp);
        if (msgimplp->im_retrans.retrans_retries == 0)
                msgimplp->im_retrans.retrans_retries = IBMF_RETRANS_DEF_RETRIES;
        if (msgimplp->im_retrans.retrans_rtv == 0)
                msgimplp->im_retrans.retrans_rtv = IBMF_RETRANS_DEF_RTV;
        if (msgimplp->im_retrans.retrans_rttv == 0)
                msgimplp->im_retrans.retrans_rttv = IBMF_RETRANS_DEF_RTTV;

        IBMF_TRACE_5(IBMF_TNF_DEBUG, DPRINT_L3, ibmf_i_msg_transport,
            IBMF_TNF_TRACE, "", "ibmf_i_msg_transport(): %s, msgp = 0x%p, "
            "class = 0x%x, method = 0x%x, attributeID = 0x%x\n",
            tnf_string, msg, "Added message", tnf_opaque, msgimplp,
            msgimplp, tnf_opaque, class, msgimplp->im_mgt_class, tnf_opaque,
            method, madhdrp->R_Method, tnf_opaque, attrib_id,
            b2h16(madhdrp->AttributeID));

        IBMF_TRACE_3(IBMF_TNF_DEBUG, DPRINT_L3, ibmf_i_msg_transport,
            IBMF_TNF_TRACE, "", "ibmf_i_msg_transport(): msgp = 0x%p, "
            "TID = 0x%p, transp_op_flags = 0x%x\n",
            tnf_opaque, msgimplp, msgimplp, tnf_opaque, tid, msgimplp->im_tid,
            tnf_uint, transp_op_flags, msgimplp->im_transp_op_flags);

        /*
         * Do not allow reuse of a message where the receive buffers are
         * being used as send buffers if this is a sequenced transaction
         */
        if ((madhdrp == msgbufp->im_bufs_mad_hdr) &&
            (msgimplp->im_transp_op_flags & IBMF_MSG_TRANS_FLAG_SEQ)) {
                IBMF_MSG_DECR_REFCNT(msgimplp);
                mutex_exit(&msgimplp->im_mutex);
                (void) sprintf(errmsg,
                    "Send and Recv buffers are the same for sequenced"
                    " transaction");
                error = B_TRUE;
                status = IBMF_REQ_INVALID;
                goto bail;
        }

        /* set transaction flags */
        if (msgimplp->im_transp_op_flags & IBMF_MSG_TRANS_FLAG_SEQ)
                msgimplp->im_flags |= IBMF_MSG_FLAGS_SEQUENCED;

        if (msgimplp->im_transp_op_flags & IBMF_MSG_TRANS_FLAG_RMPP)
                msgimplp->im_flags |= IBMF_MSG_FLAGS_SEND_RMPP;
        else
                msgimplp->im_flags |= IBMF_MSG_FLAGS_NOT_RMPP;

        /* free recv buffers if this is a reused message */
        if ((msgbufp->im_bufs_mad_hdr != NULL) &&
            (msgimplp->im_transp_op_flags & IBMF_MSG_TRANS_FLAG_SEQ)) {

                IBMF_TRACE_3(IBMF_TNF_DEBUG, DPRINT_L3, ibmf_i_msg_transport,
                    IBMF_TNF_TRACE, "", "ibmf_i_msg_transport(): %s, "
                    "msgp = 0x%p, mad_hdrp = 0x%p\n", tnf_string, msg,
                    "Freeing recv buffer for reused message",
                    tnf_opaque, msgimplp, msgimplp,
                    tnf_opaque, mad_hdr, msgbufp->im_bufs_mad_hdr);

                ibmf_i_mgt_class_to_hdr_sz_off(
                    msgbufp->im_bufs_mad_hdr->MgmtClass,
                    &cl_hdr_sz, &cl_hdr_off);

                kmem_free(msgbufp->im_bufs_mad_hdr, sizeof (ib_mad_hdr_t) +
                    cl_hdr_off + msgbufp->im_bufs_cl_hdr_len +
                    msgbufp->im_bufs_cl_data_len);

                msgbufp->im_bufs_mad_hdr = NULL;
                msgbufp->im_bufs_cl_hdr = NULL;
                msgbufp->im_bufs_cl_hdr_len = 0;
                msgbufp->im_bufs_cl_data = NULL;
                msgbufp->im_bufs_cl_data_len = 0;
        }

        mutex_exit(&msgimplp->im_mutex);

        /* initialize (and possibly allocate) the address handle */
        status = ibmf_i_alloc_ud_dest(clientp, msgimplp,
            &msgimplp->im_ud_dest, blocking);
        if (status != IBMF_SUCCESS) {
                (void) sprintf(errmsg, "ibmf_i_alloc_ud_dest() failed");
                error = B_TRUE;
                goto bail;
        }

        _NOTE(NOW_VISIBLE_TO_OTHER_THREADS(*msgimplp, *msgbufp))

        /* add the message to the client context's message list */
        ibmf_i_client_add_msg(clientp, msgimplp);

        mutex_enter(&msgimplp->im_mutex);

        /* no one should have touched our state */
        ASSERT(msgimplp->im_trans_state_flags == IBMF_TRANS_STATE_FLAG_UNINIT);

        /* transition out of uninit state */
        msgimplp->im_trans_state_flags = IBMF_TRANS_STATE_FLAG_INIT;

        IBMF_TRACE_5(IBMF_TNF_DEBUG, DPRINT_L3, ibmf_i_msg_transport,
            IBMF_TNF_TRACE, "", "ibmf_i_msg_transport(): msgp = 0x%p, "
            "local_lid = 0x%x, remote_lid = 0x%x, remote_qpn = 0x%x, "
            "block = %d\n", tnf_opaque, msgp, msgimplp,
            tnf_uint, local_lid, msgimplp->im_local_addr.ia_local_lid,
            tnf_uint, remote_lid, msgimplp->im_local_addr.ia_remote_lid,
            tnf_uint, remote_qpn, msgimplp->im_local_addr.ia_remote_qno,
            tnf_uint, blocking, blocking);

        IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L3, ibmf_i_msg_transport,
            IBMF_TNF_TRACE, "", "ibmf_i_msg_transport(): "
            "unsetting timer %p %d\n", tnf_opaque, msgimplp, msgimplp,
            tnf_opaque, timeout_id, msgimplp->im_rp_timeout_id);

        ASSERT(msgimplp->im_rp_timeout_id == 0);
        ASSERT(msgimplp->im_tr_timeout_id == 0);

        if ((msgimplp->im_transp_op_flags & IBMF_MSG_TRANS_FLAG_RMPP) == 0) {

                /* Non-RMPP transaction */

                status = ibmf_i_send_single_pkt(clientp, ibmf_qp_handle,
                    msgimplp, blocking);
                if (status != IBMF_SUCCESS) {
                        IBMF_MSG_DECR_REFCNT(msgimplp);
                        mutex_exit(&msgimplp->im_mutex);
                        ibmf_i_client_rem_msg(clientp, msgimplp, &refcnt);
                        (void) sprintf(errmsg, "Single packet send failed");
                        error = B_TRUE;
                        goto bail;
                }

        } else if (msgimplp->im_transp_op_flags & IBMF_MSG_TRANS_FLAG_RMPP) {

                /* RMPP transaction */

                /* check if client supports RMPP traffic */
                if ((clientp->ic_reg_flags & IBMF_REG_FLAG_RMPP) == 0) {
                        IBMF_MSG_DECR_REFCNT(msgimplp);
                        mutex_exit(&msgimplp->im_mutex);
                        ibmf_i_client_rem_msg(clientp, msgimplp, &refcnt);
                        (void) sprintf(errmsg, "Class does not support RMPP");
                        error = B_TRUE;
                        status = IBMF_BAD_RMPP_OPT;
                        goto bail;
                }

                /* for non-special QPs, check if QP supports RMPP traffic */
                if (ibmf_qp_handle != IBMF_QP_HANDLE_DEFAULT &&
                    (((ibmf_alt_qp_t *)ibmf_qp_handle)->isq_supports_rmpp ==
                    B_FALSE)) {
                        IBMF_MSG_DECR_REFCNT(msgimplp);
                        mutex_exit(&msgimplp->im_mutex);
                        ibmf_i_client_rem_msg(clientp, msgimplp, &refcnt);
                        (void) sprintf(errmsg, "QP does not support RMPP");
                        error = B_TRUE;
                        status = IBMF_BAD_RMPP_OPT;
                        goto bail;
                }

                /* check if transaction is "double sided" (send and receive) */
                if (msgimplp->im_transp_op_flags & IBMF_MSG_TRANS_FLAG_SEQ)
                        isDS = 1;

                status = ibmf_i_send_rmpp_pkts(clientp, ibmf_qp_handle,
                    msgimplp, isDS, blocking);
                if (status != IBMF_SUCCESS) {
                        IBMF_MSG_DECR_REFCNT(msgimplp);
                        mutex_exit(&msgimplp->im_mutex);
                        ibmf_i_client_rem_msg(clientp, msgimplp, &refcnt);
                        (void) sprintf(errmsg, "RMPP packets send failed");
                        error = B_TRUE;
                        goto bail;
                }
        }

        /*
         * decrement the reference count so notify_client() can remove the
         * message when it's ready
         */
        IBMF_MSG_DECR_REFCNT(msgimplp);

        /* check if the transaction is a blocking transaction */
        if (blocking && ((msgimplp->im_trans_state_flags &
            IBMF_TRANS_STATE_FLAG_SIGNALED) == 0)) {

                /* indicate that the tranaction is waiting */
                msgimplp->im_trans_state_flags |= IBMF_TRANS_STATE_FLAG_WAIT;

                IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L3, ibmf_i_msg_transport,
                    IBMF_TNF_TRACE, "",
                    "ibmf_i_msg_transport(): %s, msgp = 0x%p\n",
                    tnf_string, msg, "blocking for completion",
                    tnf_opaque, msgimplp, msgimplp);

                /* wait for transaction completion */
                cv_wait(&msgimplp->im_trans_cv, &msgimplp->im_mutex);

                IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L3, ibmf_i_msg_transport,
                    IBMF_TNF_TRACE, "",
                    "ibmf_i_msg_transport(): %s, msgp = 0x%p\n",
                    tnf_string, msg, "unblocking for completion",
                    tnf_opaque, msgimplp, msgimplp);

                /* clean up flags */
                msgimplp->im_trans_state_flags &= ~IBMF_TRANS_STATE_FLAG_WAIT;
                msgimplp->im_flags &= ~IBMF_MSG_FLAGS_BUSY;

                if (msgimplp->im_msg_status != IBMF_SUCCESS) {

                        IBMF_TRACE_1(IBMF_TNF_NODEBUG, DPRINT_L1,
                            ibmf_i_msg_transport_err, IBMF_TNF_ERROR, "",
                            "ibmf_i_msg_transport(): msg_status = %d\n",
                            tnf_uint, msgstatus, msgimplp->im_msg_status);

                        status = msgimplp->im_msg_status;
                }
        } else if (blocking && (msgimplp->im_trans_state_flags &
            IBMF_TRANS_STATE_FLAG_SIGNALED)) {
                msgimplp->im_flags &= ~IBMF_MSG_FLAGS_BUSY;

                if (msgimplp->im_msg_status != IBMF_SUCCESS) {
                        IBMF_TRACE_1(IBMF_TNF_NODEBUG, DPRINT_L1,
                            ibmf_i_msg_transport_err, IBMF_TNF_ERROR, "",
                            "ibmf_i_msg_transport(): msg_status = %d\n",
                            tnf_uint, msgstatus, msgimplp->im_msg_status);
                        status = msgimplp->im_msg_status;
                }
        }

        msg_rp_unset_id = msg_tr_unset_id = 0;
        msg_rp_unset_id = msgimplp->im_rp_unset_timeout_id;
        msg_tr_unset_id = msgimplp->im_tr_unset_timeout_id;
        msgimplp->im_rp_unset_timeout_id = 0;
        msgimplp->im_tr_unset_timeout_id = 0;

        mutex_exit(&msgimplp->im_mutex);

        /* Unset the timers */
        if (msg_rp_unset_id != 0) {
                (void) untimeout(msg_rp_unset_id);
        }

        if (msg_tr_unset_id != 0) {
                (void) untimeout(msg_tr_unset_id);
        }

        /* increment kstats of the number of sent messages */
        mutex_enter(&clientp->ic_kstat_mutex);
        IBMF_ADD32_KSTATS(clientp, msgs_sent, 1);
        mutex_exit(&clientp->ic_kstat_mutex);

bail:
        if (error) {
                IBMF_TRACE_2(IBMF_TNF_NODEBUG, DPRINT_L1,
                    ibmf_i_msg_transport_err, IBMF_TNF_ERROR, "",
                    "ibmf_i_msg_transport(): %s, msgp = 0x%p\n",
                    tnf_string, msg, errmsg, tnf_opaque, msgimplp, msgimplp);
        }

        IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_msg_transport_end,
            IBMF_TNF_TRACE, "", "ibmf_i_msg_transport() exit, status = %d\n",
            tnf_uint, status, status);

        return (status);
}

/*
 * ibmf_i_init_msg():
 *      Initialize the message fields
 */
void
ibmf_i_init_msg(ibmf_msg_impl_t *msgimplp, ibmf_msg_cb_t trans_cb,
    void *trans_cb_arg, ibmf_retrans_t *retrans, boolean_t block)
{
        IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_init_msg_start,
            IBMF_TNF_TRACE, "", "ibmf_i_init_msg() enter\n");

        _NOTE(ASSUMING_PROTECTED(msgimplp->im_trans_cb,
            msgimplp->im_trans_cb_arg))

        if (block == B_TRUE)
                msgimplp->im_msg_flags |= IBMF_MSG_FLAGS_BLOCKING;
        msgimplp->im_trans_cb = trans_cb;
        msgimplp->im_trans_cb_arg = trans_cb_arg;

        bzero(&msgimplp->im_retrans, sizeof (ibmf_retrans_t));
        if (retrans != NULL) {
                bcopy((void *)retrans, (void *)&msgimplp->im_retrans,
                    sizeof (ibmf_retrans_t));
        }

        IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_init_msg_end,
            IBMF_TNF_TRACE, "", "ibmf_i_init_msg() exit\n");
}

/*
 * ibmf_i_alloc_qp():
 *      Allocate a QP context for the alternate QPs
 */
int
ibmf_i_alloc_qp(ibmf_client_t *clientp, ib_pkey_t p_key, ib_qkey_t q_key,
    uint_t flags, ibmf_qp_handle_t *ibmf_qp_handlep)
{
        ibmf_ci_t               *ibmf_cip = clientp->ic_myci;
        ibt_qp_alloc_attr_t     qp_attrs;
        ibt_qp_info_t           qp_modify_attr;
        ibmf_alt_qp_t           *qp_ctx;
        uint16_t                pkey_ix;
        ibt_status_t            ibt_status;
        int                     i, blocking;
        boolean_t               error = B_FALSE;
        int                     status = IBMF_SUCCESS;
        char                    errmsg[128];


        IBMF_TRACE_3(IBMF_TNF_DEBUG, DPRINT_L4,
            ibmf_i_alloc_qp_start, IBMF_TNF_TRACE, "",
            "ibmf_i_alloc_qp() enter, clientp = %p, pkey = %x, qkey = %x \n",
            tnf_opaque, clientp, clientp, tnf_uint, p_key, p_key,
            tnf_uint, q_key, q_key);

        /*
         * get the pkey index associated with this pkey if present in table
         */
        if (ibmf_i_get_pkeyix(clientp->ic_ci_handle, p_key,
            clientp->ic_client_info.port_num, &pkey_ix) != IBMF_SUCCESS) {
                (void) sprintf(errmsg, "pkey not in table, pkey = %x", p_key);
                error = B_TRUE;
                status = IBMF_FAILURE;
                goto bail;
        }

        /* allocate QP context memory */
        qp_ctx = (ibmf_alt_qp_t *)kmem_zalloc(sizeof (ibmf_alt_qp_t),
            (flags & IBMF_ALLOC_SLEEP) ? KM_SLEEP : KM_NOSLEEP);
        if (qp_ctx == NULL) {
                (void) sprintf(errmsg, "failed to kmem_zalloc qp ctx");
                error = B_TRUE;
                status = IBMF_NO_RESOURCES;
                goto bail;
        }

        _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*qp_ctx));

        /* setup the qp attrs for the alloc call */
        qp_attrs.qp_scq_hdl = ibmf_cip->ci_alt_cq_handle;
        qp_attrs.qp_rcq_hdl = ibmf_cip->ci_alt_cq_handle;
        qp_attrs.qp_pd_hdl = ibmf_cip->ci_pd;
        qp_attrs.qp_sizes.cs_sq_sgl = IBMF_MAX_SQ_WR_SGL_ELEMENTS;
        qp_attrs.qp_sizes.cs_rq_sgl = IBMF_MAX_RQ_WR_SGL_ELEMENTS;
        qp_attrs.qp_sizes.cs_sq = ibmf_send_wqes_posted_per_qp;
        qp_attrs.qp_sizes.cs_rq = ibmf_recv_wqes_posted_per_qp;
        qp_attrs.qp_flags = IBT_ALL_SIGNALED;
        qp_attrs.qp_alloc_flags = IBT_QP_NO_FLAGS;

        /* request IBT for a qp with the desired attributes */
        ibt_status = ibt_alloc_qp(clientp->ic_ci_handle, IBT_UD_RQP,
            &qp_attrs, &qp_ctx->isq_qp_sizes, &qp_ctx->isq_qpn,
            &qp_ctx->isq_qp_handle);
        if (ibt_status != IBT_SUCCESS) {
                kmem_free(qp_ctx, sizeof (ibmf_alt_qp_t));
                (void) sprintf(errmsg, "failed to alloc qp, status = %d",
                    ibt_status);
                error = B_TRUE;
                status = IBMF_NO_RESOURCES;
                goto bail;
        }

        qp_modify_attr.qp_trans = IBT_UD_SRV;
        qp_modify_attr.qp_flags = IBT_CEP_NO_FLAGS;
        qp_modify_attr.qp_transport.ud.ud_qkey = q_key;
        qp_modify_attr.qp_transport.ud.ud_sq_psn = 0;
        qp_modify_attr.qp_transport.ud.ud_pkey_ix = pkey_ix;
        qp_modify_attr.qp_transport.ud.ud_port =
            clientp->ic_client_info.port_num;

        /* Set up the client handle in the QP context */
        qp_ctx->isq_client_hdl = clientp;

        /* call the IB transport to initialize the QP */
        ibt_status = ibt_initialize_qp(qp_ctx->isq_qp_handle, &qp_modify_attr);
        if (ibt_status != IBT_SUCCESS) {
                (void) ibt_free_qp(qp_ctx->isq_qp_handle);
                kmem_free(qp_ctx, sizeof (ibmf_alt_qp_t));
                (void) sprintf(errmsg, "failed to initialize qp, status = %d",
                    ibt_status);
                error = B_TRUE;
                status = IBMF_NO_RESOURCES;
                goto bail;
        }

        /* Set up the WQE caches */
        status = ibmf_i_init_altqp_wqes(qp_ctx);
        if (status != IBMF_SUCCESS) {
                (void) ibt_free_qp(qp_ctx->isq_qp_handle);
                kmem_free(qp_ctx, sizeof (ibmf_alt_qp_t));
                (void) sprintf(errmsg, "failed to init wqe caches, status = %d",
                    status);
                error = B_TRUE;
                goto bail;
        }

        qp_ctx->isq_next = NULL;
        qp_ctx->isq_pkey = p_key;
        qp_ctx->isq_qkey = q_key;
        qp_ctx->isq_port_num = clientp->ic_client_info.port_num;
        mutex_init(&qp_ctx->isq_mutex, NULL, MUTEX_DRIVER, NULL);
        mutex_init(&qp_ctx->isq_wqe_mutex, NULL, MUTEX_DRIVER, NULL);
        cv_init(&qp_ctx->isq_recv_cb_teardown_cv, NULL, CV_DRIVER, NULL);
        cv_init(&qp_ctx->isq_sqd_cv, NULL, CV_DRIVER, NULL);
        cv_init(&qp_ctx->isq_wqes_cv, NULL, CV_DRIVER, NULL);

        _NOTE(NOW_VISIBLE_TO_OTHER_THREADS(*qp_ctx));

        /* add alt qp to the list in CI context */
        mutex_enter(&ibmf_cip->ci_mutex);
        if (ibmf_cip->ci_alt_qp_list == NULL) {
                ibmf_cip->ci_alt_qp_list = qp_ctx;
        } else {
                ibmf_alt_qp_t *qpp;

                qpp = ibmf_cip->ci_alt_qp_list;
                while (qpp->isq_next != NULL) {
                        qpp = qpp->isq_next;
                }
                qpp->isq_next = qp_ctx;
        }
        mutex_exit(&ibmf_cip->ci_mutex);

        *ibmf_qp_handlep = (ibmf_qp_handle_t)qp_ctx;

        if (flags & IBMF_ALLOC_SLEEP)
                blocking = 1;
        else
                blocking = 0;

        /* post the max number of buffers to RQ */
        for (i = 0; i < ibmf_recv_wqes_per_port; i++) {
                status = ibmf_i_post_recv_buffer(ibmf_cip, clientp->ic_qp,
                    blocking, *ibmf_qp_handlep);
                if (status != IBMF_SUCCESS) {
                        IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L3,
                            ibmf_i_alloc_qp, IBMF_TNF_TRACE, "",
                            "ibmf_i_alloc_qp(): %s, status = %d\n",
                            tnf_string, msg, "ibmf_i_post_recv_buffer() failed",
                            tnf_int, status, status);
                }
        }

        mutex_enter(&clientp->ic_kstat_mutex);
        IBMF_ADD32_KSTATS(clientp, alt_qps_alloced, 1);
        mutex_exit(&clientp->ic_kstat_mutex);

bail:
        if (error) {
                IBMF_TRACE_1(IBMF_TNF_NODEBUG, DPRINT_L1,
                    ibmf_i_alloc_qp_err, IBMF_TNF_TRACE, "",
                    "ibmf_i_alloc_qp(): %s\n", tnf_string, msg, errmsg);
        }

        IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_alloc_qp_end,
            IBMF_TNF_TRACE, "", "ibmf_i_alloc_qp() exit, qp = %p\n",
            tnf_opaque, qp_handlep, *ibmf_qp_handlep);
        return (status);
}

/*
 * ibmf_i_free_qp():
 *      Free an alternate QP context
 */
/* ARGSUSED */
int
ibmf_i_free_qp(ibmf_qp_handle_t ibmf_qp_handle, uint_t flags)
{
        ibmf_alt_qp_t           *qp_ctx = (ibmf_alt_qp_t *)ibmf_qp_handle;
        ibmf_client_t           *clientp = qp_ctx->isq_client_hdl;
        ibmf_ci_t               *ibmf_cip = qp_ctx->isq_client_hdl->ic_myci;
        ibmf_alt_qp_t           *qpp, *pqpp;
        ibt_status_t            ibt_status;

        IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L4,
            ibmf_i_free_qp_start, IBMF_TNF_TRACE, "",
            "ibmf_i_free_qp() enter, qp_hdl = %p, flags = %x\n",
            tnf_opaque, qp_hdl, ibmf_qp_handle, tnf_uint, flags, flags);

        /* remove qp from the list in CI context */

        mutex_enter(&ibmf_cip->ci_mutex);
        qpp = ibmf_cip->ci_alt_qp_list;
        ASSERT(qpp != NULL);
        if (qpp == qp_ctx) {
                ibmf_cip->ci_alt_qp_list = qpp->isq_next;
        } else {
                while (qpp != NULL) {
                        if (qpp == qp_ctx)
                                break;
                        pqpp = qpp;
                        qpp = qpp->isq_next;
                }
                ASSERT(qpp != NULL);
                pqpp->isq_next = qpp->isq_next;
        }

        mutex_exit(&ibmf_cip->ci_mutex);

        /* flush the WQEs in the QP queues */
        ibt_status = ibt_flush_qp(qp_ctx->isq_qp_handle);
        if (ibt_status != IBT_SUCCESS) {
                IBMF_TRACE_2(IBMF_TNF_NODEBUG, DPRINT_L1,
                    ibmf_i_free_qp_err, IBMF_TNF_TRACE, "",
                    "ibmf_i_free_qp(): %s, status = %d\n",
                    tnf_string, msg, "failed to close qp",
                    tnf_uint, ibt_status, ibt_status);
                IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_free_qp_end,
                    IBMF_TNF_TRACE, "", "ibmf_i_free_qp() exit\n");
                return (IBMF_TRANSPORT_FAILURE);
        }

        /* Call the MAD completion handler */
        ibmf_i_mad_completions(ibmf_cip->ci_alt_cq_handle, (void*)ibmf_cip);

        /* Wait here for all WQE owned by this QP to get freed */
        mutex_enter(&qpp->isq_mutex);
        while (qpp->isq_wqes_alloced != 0) {
                cv_wait(&qpp->isq_wqes_cv, &qpp->isq_mutex);
        }
        mutex_exit(&qpp->isq_mutex);

        cv_destroy(&qp_ctx->isq_recv_cb_teardown_cv);
        cv_destroy(&qp_ctx->isq_sqd_cv);
        cv_destroy(&qp_ctx->isq_wqes_cv);

        /* call the IB transport to free the QP */
        ibt_status = ibt_free_qp(qp_ctx->isq_qp_handle);
        if (ibt_status != IBT_SUCCESS) {
                IBMF_TRACE_2(IBMF_TNF_NODEBUG, DPRINT_L1,
                    ibmf_i_free_qp_err, IBMF_TNF_TRACE, "",
                    "ibmf_i_free_qp(): %s, status = %d\n",
                    tnf_string, msg, "failed to free qp",
                    tnf_uint, ibt_status, ibt_status);
                IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_free_qp_end,
                    IBMF_TNF_TRACE, "", "ibmf_i_free_qp() exit\n");
                return (IBMF_TRANSPORT_FAILURE);
        }

        /* Clean up the WQE caches */
        ibmf_i_fini_altqp_wqes(qp_ctx);
        mutex_destroy(&qp_ctx->isq_wqe_mutex);
        mutex_destroy(&qp_ctx->isq_mutex);

        mutex_enter(&clientp->ic_kstat_mutex);
        IBMF_SUB32_KSTATS(clientp, alt_qps_alloced, 1);
        mutex_exit(&clientp->ic_kstat_mutex);

        kmem_free(qp_ctx, sizeof (ibmf_alt_qp_t));

        IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_free_qp_end,
            IBMF_TNF_TRACE, "", "ibmf_i_free_qp() exit\n");

        return (IBMF_SUCCESS);
}

/*
 * ibmf_i_query_qp():
 *      Query an alternate QP context
 */
/* ARGSUSED */
int
ibmf_i_query_qp(ibmf_qp_handle_t ibmf_qp_handle, uint_t flags,
    uint_t *qp_nump, ib_pkey_t *p_keyp, ib_qkey_t *q_keyp, uint8_t *portnump)
{
        ibt_qp_query_attr_t     qp_query;
        ibmf_alt_qp_t           *qp_ctx = (ibmf_alt_qp_t *)ibmf_qp_handle;
        uint16_t                pkey_ix;
        ibt_status_t            ibt_status;

        IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L4,
            ibmf_i_free_qp_start, IBMF_TNF_TRACE, "",
            "ibmf_i_free_qp() enter, qp_hdl = %p, flags = %x\n",
            tnf_opaque, qp_hdl, ibmf_qp_handle, tnf_uint, flags, flags);

        ibt_status = ibt_query_qp(qp_ctx->isq_qp_handle, &qp_query);
        if (ibt_status != IBT_SUCCESS) {
                IBMF_TRACE_2(IBMF_TNF_NODEBUG, DPRINT_L1,
                    ibmf_i_query_qp_err, IBMF_TNF_TRACE, "",
                    "ibmf_i_query_qp(): %s, status = %d\n",
                    tnf_string, msg, "failed to query qp",
                    tnf_uint, ibt_status, ibt_status);
                IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_query_qp_end,
                    IBMF_TNF_TRACE, "", "ibmf_i_query_qp() exit\n");
                return (IBMF_TRANSPORT_FAILURE);
        }

        /* move the desired attributes into the locations provided */
        *qp_nump = qp_query.qp_qpn;
        *q_keyp = qp_query.qp_info.qp_transport.ud.ud_qkey;
        *portnump = qp_query.qp_info.qp_transport.ud.ud_port;

        pkey_ix = qp_query.qp_info.qp_transport.ud.ud_pkey_ix;

        /* get the pkey based on the pkey_ix */
        ibt_status = ibt_index2pkey(qp_ctx->isq_client_hdl->ic_ci_handle,
            *portnump, pkey_ix, p_keyp);
        if (ibt_status != IBT_SUCCESS) {
                IBMF_TRACE_3(IBMF_TNF_NODEBUG, DPRINT_L1,
                    ibmf_i_query_qp_err, IBMF_TNF_TRACE, "",
                    "ibmf_i_query_qp(): %s, pkey_ix = %d, status = %d\n",
                    tnf_string, msg, "failed to get pkey from index",
                    tnf_uint, pkey_ix, pkey_ix,
                    tnf_uint, ibt_status, ibt_status);
                IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_query_qp_end,
                    IBMF_TNF_TRACE, "", "ibmf_i_query_qp() exit\n");
                return (IBMF_TRANSPORT_FAILURE);
        }

        IBMF_TRACE_4(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_query_qp_end,
            IBMF_TNF_TRACE, "", "ibmf_i_query_qp() exit, qp_num = 0x%x, "
            "pkey = 0x%x, qkey = 0x%x, portnum = %d\n",
            tnf_uint, qp_num, *qp_nump, tnf_uint, pkey, *p_keyp,
            tnf_uint, qkey, *q_keyp, tnf_uint, portnum, *portnump);

        return (IBMF_SUCCESS);
}

/*
 * ibmf_i_modify_qp():
 *      Modify an alternate QP context
 */
/* ARGSUSED */
int
ibmf_i_modify_qp(ibmf_qp_handle_t ibmf_qp_handle, ib_pkey_t p_key,
    ib_qkey_t q_key, uint_t flags)
{
        ibmf_alt_qp_t           *qp_ctx = (ibmf_alt_qp_t *)ibmf_qp_handle;
        ibmf_client_t           *clientp = qp_ctx->isq_client_hdl;
        ibmf_ci_t               *ibmf_cip = clientp->ic_myci;
        ibmf_alt_qp_t           *qpp;
        ibt_qp_info_t           qp_mod;
        ibt_cep_modify_flags_t  qp_mod_flags;
        ibt_queue_sizes_t       actual_sz;
        uint16_t                pkey_ix;
        ibt_status_t            ibt_status;

        IBMF_TRACE_4(IBMF_TNF_DEBUG, DPRINT_L4,
            ibmf_i_modify_qp_start, IBMF_TNF_TRACE, "",
            "ibmf_i_modify_qp() enter, qp_hdl = %p, flags = %x, pkey = 0x%x, "
            "qkey = 0x%x\n", tnf_opaque, qp_hdl, ibmf_qp_handle,
            tnf_uint, flags, flags, tnf_uint, p_key, p_key,
            tnf_uint, q_key, q_key);

        /*
         * get the pkey index associated with this pkey if present in table
         */
        if (ibmf_i_get_pkeyix(clientp->ic_ci_handle, p_key,
            clientp->ic_client_info.port_num, &pkey_ix) != IBMF_SUCCESS) {
                IBMF_TRACE_2(IBMF_TNF_NODEBUG, DPRINT_L1,
                    ibmf_i_modify_qp_err, IBMF_TNF_TRACE, "",
                    "ibmf_i_modify_qp(): %s, pkey = %x\n",
                    tnf_string, msg, "pkey not in table",
                    tnf_uint, pkey, p_key);
                IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_modify_qp_end,
                    IBMF_TNF_TRACE, "", "ibmf_i_modify_qp() exit\n");
                return (IBMF_FAILURE);
        }

        /* Find the QP context in the CI QP context list */
        mutex_enter(&ibmf_cip->ci_mutex);
        qpp = ibmf_cip->ci_alt_qp_list;
        while (qpp != NULL) {
                if (qpp == qp_ctx) {
                        break;
                }
                qpp = qpp->isq_next;
        }

        if (qpp == NULL) {
                mutex_exit(&ibmf_cip->ci_mutex);

                IBMF_TRACE_1(IBMF_TNF_NODEBUG, DPRINT_L1,
                    ibmf_i_modify_qp_err, IBMF_TNF_TRACE, "",
                    "ibmf_i_modify_qp(): %s\n",
                    tnf_string, msg, "QP not in altqp list");
                IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_modify_qp_end,
                    IBMF_TNF_TRACE, "", "ibmf_i_modify_qp() exit\n");
                return (IBMF_BAD_QP_HANDLE);

        } else {

                mutex_enter(&qp_ctx->isq_mutex);
        }

        mutex_exit(&ibmf_cip->ci_mutex);

        /*
         * Transition the QP to SQD state
         */
        bzero(&qp_mod, sizeof (ibt_qp_info_t));
        qp_mod.qp_trans = IBT_UD_SRV;
        qp_mod.qp_state = IBT_STATE_SQD;
        qp_mod_flags = IBT_CEP_SET_STATE | IBT_CEP_SET_SQD_EVENT;
        ibt_status = ibt_modify_qp(qp_ctx->isq_qp_handle, qp_mod_flags,
            &qp_mod, &actual_sz);
        if (ibt_status != IBT_SUCCESS) {
                mutex_exit(&qp_ctx->isq_mutex);
                IBMF_TRACE_2(IBMF_TNF_NODEBUG, DPRINT_L1,
                    ibmf_i_modify_qp_err, IBMF_TNF_TRACE, "",
                    "ibmf_i_modify_qp(): %s, qp_hdl = %p\n",
                    tnf_string, msg, "QP transition RTS to SQD failed",
                    tnf_opaque, qp_handle, qp_ctx->isq_qp_handle);
                IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_modify_qp_end,
                    IBMF_TNF_TRACE, "", "ibmf_i_modify_qp() exit\n");
                return (IBMF_TRANSPORT_FAILURE);
        }

        /*
         * Wait for an event indicating that the QP is in SQD state
         */
        cv_wait(&qp_ctx->isq_sqd_cv, &qp_ctx->isq_mutex);

        /* Setup QP modification information for transition to RTS state */
        bzero(&qp_mod, sizeof (ibt_qp_info_t));
        qp_mod.qp_trans = IBT_UD_SRV;
        qp_mod.qp_state = IBT_STATE_RTS;
        qp_mod.qp_current_state = IBT_STATE_SQD;
        qp_mod.qp_transport.ud.ud_pkey_ix = pkey_ix;
        qp_mod.qp_transport.ud.ud_qkey = q_key;
        qp_mod_flags = IBT_CEP_SET_STATE | IBT_CEP_SET_PKEY_IX |
            IBT_CEP_SET_QKEY;

        /*
         * transition the QP back to RTS state to allow
         * modification of the pkey and qkey
         */

        ibt_status = ibt_modify_qp(qp_ctx->isq_qp_handle, qp_mod_flags,
            &qp_mod, &actual_sz);
        if (ibt_status != IBT_SUCCESS) {
                mutex_exit(&qp_ctx->isq_mutex);
                IBMF_TRACE_3(IBMF_TNF_NODEBUG, DPRINT_L1,
                    ibmf_i_modify_qp_err, IBMF_TNF_TRACE, "",
                    "ibmf_i_modify_qp(): %s, qp_hdl = %p, status = %d\n",
                    tnf_string, msg, "QP transition SQD to RTS failed",
                    tnf_opaque, qp_handle, qp_ctx->isq_qp_handle,
                    tnf_uint, ibt_status, ibt_status);
                IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_modify_qp_end,
                    IBMF_TNF_TRACE, "", "ibmf_i_modify_qp() exit\n");
                return (IBMF_TRANSPORT_FAILURE);
        }

        qp_ctx->isq_pkey = p_key;
        qp_ctx->isq_qkey = q_key;
        mutex_exit(&qp_ctx->isq_mutex);

        IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_modify_qp_end,
            IBMF_TNF_TRACE, "", "ibmf_i_modify_qp() exit\n");
        return (IBMF_SUCCESS);
}

/*
 * ibmf_i_post_recv_buffer():
 *      Post a WQE to the RQ of the specified QP
 */
int
ibmf_i_post_recv_buffer(ibmf_ci_t *cip, ibmf_qp_t *qpp, boolean_t block,
    ibmf_qp_handle_t ibmf_qp_handle)
{
        int                     ret;
        ibt_wr_ds_t             *sgl;
        ibt_status_t            status;
        ibmf_recv_wqe_t         *recv_wqep;
        ibt_qp_hdl_t            ibt_qp_handle;
        struct kmem_cache       *kmem_cachep;
        ibmf_alt_qp_t           *altqp;

        IBMF_TRACE_4(IBMF_TNF_DEBUG, DPRINT_L4,
            ibmf_i_post_recv_buffer_start, IBMF_TNF_TRACE, "",
            "ibmf_i_post_recv_buffer() enter, cip = %p, qpp = %p, "
            "qp_hdl = %p, block = %d\n", tnf_opaque, cip, cip,
            tnf_opaque, qpp, qpp, tnf_opaque, qp_hdl, ibmf_qp_handle,
            tnf_uint, block, block);

        /*
         * if we haven't hit the max wqes per qp, attempt to allocate a recv
         * wqe and post it to the recv queue.
         * It is possible for more than one thread to get through this
         * check below and post wqes that could push us above the
         * ibmf_recv_wqes_posted_per_qp. We catch that case when the recv
         * completion is signaled.
         */
        ASSERT(MUTEX_NOT_HELD(&cip->ci_mutex));

        /* Get the WQE kmem cache pointer based on the QP type */
        if (ibmf_qp_handle == IBMF_QP_HANDLE_DEFAULT)
                kmem_cachep = cip->ci_recv_wqes_cache;
        else {
                altqp = (ibmf_alt_qp_t *)ibmf_qp_handle;
                kmem_cachep = altqp->isq_recv_wqes_cache;
        }

        /* allocate a receive WQE from the receive WQE kmem cache */
        recv_wqep = kmem_cache_alloc(kmem_cachep,
            (block == B_TRUE ? KM_SLEEP : KM_NOSLEEP));
        if (recv_wqep == NULL) {
                /*
                 * Attempt to extend the cache and then retry the
                 * kmem_cache_alloc()
                 */
                if (ibmf_i_extend_wqe_cache(cip, ibmf_qp_handle, block) ==
                    IBMF_NO_RESOURCES) {
                        mutex_enter(&cip->ci_mutex);
                        IBMF_ADD32_PORT_KSTATS(cip, rwqe_allocs_failed, 1);
                        mutex_exit(&cip->ci_mutex);
                        IBMF_TRACE_2(IBMF_TNF_NODEBUG, DPRINT_L1,
                            ibmf_i_post_recv_buffer_err, IBMF_TNF_ERROR, "",
                            "ibmf_i_post_recv_buffer(): %s, status = %d\n",
                            tnf_string, msg, "alloc recv_wqe failed",
                            tnf_int, ibmf_status, IBMF_NO_RESOURCES);
                        IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
                            ibmf_i_post_recv_buffer_end, IBMF_TNF_TRACE, "",
                            "ibmf_i_post_recv_buffer() exit\n");
                        return (IBMF_NO_RESOURCES);
                } else {
                        recv_wqep = kmem_cache_alloc(kmem_cachep,
                            (block == B_TRUE ? KM_SLEEP : KM_NOSLEEP));
                        if (recv_wqep == NULL) {
                                /* Allocation failed again. Give up here. */
                                mutex_enter(&cip->ci_mutex);
                                IBMF_ADD32_PORT_KSTATS(cip, rwqe_allocs_failed,
                                    1);
                                mutex_exit(&cip->ci_mutex);
                                IBMF_TRACE_2(IBMF_TNF_NODEBUG, DPRINT_L1,
                                    ibmf_i_post_recv_buffer_err,
                                    IBMF_TNF_ERROR, "",
                                    "ibmf_i_post_recv_buffer(): %s, "
                                    "status = %d\n",
                                    tnf_string, msg, "alloc recv_wqe failed",
                                    tnf_int, ibmf_status, IBMF_NO_RESOURCES);
                                IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
                                    ibmf_i_post_recv_buffer_end,
                                    IBMF_TNF_TRACE, "",
                                    "ibmf_i_post_recv_buffer() exit\n");
                                return (IBMF_NO_RESOURCES);
                        }
                }
        }

        _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*recv_wqep))

        /*
         * if the qp handle provided in ibmf_send_pkt() or
         * ibmf_setup_recv_cb() is not the default qp handle
         * for this client, then the wqe must be queued on this qp,
         * else use the default qp handle set up during ibmf_register()
         */
        if (ibmf_qp_handle == IBMF_QP_HANDLE_DEFAULT) {
                ibt_qp_handle = qpp->iq_qp_handle;
        } else {
                ibt_qp_handle =
                    ((ibmf_alt_qp_t *)ibmf_qp_handle)->isq_qp_handle;
        }

        /* allocate memory for the scatter-gather list */
        sgl = kmem_zalloc(IBMF_MAX_RQ_WR_SGL_ELEMENTS * sizeof (ibt_wr_ds_t),
            (block == B_TRUE) ? KM_SLEEP : KM_NOSLEEP);
        if (sgl == NULL) {
                kmem_cache_free(kmem_cachep, recv_wqep);
                IBMF_TRACE_1(IBMF_TNF_NODEBUG, DPRINT_L1,
                    ibmf_i_post_recv_buffer_err, IBMF_TNF_ERROR, "",
                    "ibmf_i_post_recv_buffer(): %s\n",
                    tnf_string, msg, "failed to kmem_zalloc qp ctx");
                IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
                    ibmf_i_post_recv_buffer_end, IBMF_TNF_TRACE, "",
                    "ibmf_i_post_recv_buffer() exit\n");
                return (IBMF_NO_RESOURCES);
        }

        /* initialize it */
        ibmf_i_init_recv_wqe(qpp, sgl, recv_wqep, ibt_qp_handle,
            ibmf_qp_handle);

        _NOTE(NOW_VISIBLE_TO_OTHER_THREADS(*recv_wqep))

        /* and post it */
        status = ibt_post_recv(recv_wqep->recv_qp_handle, &recv_wqep->recv_wr,
            1, NULL);

        ret = ibmf_i_ibt_to_ibmf_status(status);
        if (ret != IBMF_SUCCESS) {
                kmem_free(sgl, IBMF_MAX_RQ_WR_SGL_ELEMENTS *
                    sizeof (ibt_wr_ds_t));
                kmem_cache_free(kmem_cachep, recv_wqep);
                IBMF_TRACE_2(IBMF_TNF_NODEBUG, DPRINT_L1,
                    ibmf_i_post_recv_buffer_err, IBMF_TNF_ERROR, "",
                    "ibmf_i_post_recv_buffer(): %s, status = %d\n",
                    tnf_string, msg, "ibt_post_recv failed",
                    tnf_uint, ibt_status, status);
                IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
                    ibmf_i_post_recv_buffer_end, IBMF_TNF_TRACE, "",
                    "ibmf_i_post_recv_buffer() exit\n");
                return (ret);
        }

        mutex_enter(&cip->ci_mutex);
        IBMF_ADD32_PORT_KSTATS(cip, recv_wqes_alloced, 1);
        mutex_exit(&cip->ci_mutex);
        if (ibmf_qp_handle == IBMF_QP_HANDLE_DEFAULT) {
                mutex_enter(&qpp->iq_mutex);
                qpp->iq_rwqes_posted++;
                mutex_exit(&qpp->iq_mutex);
                mutex_enter(&cip->ci_mutex);
                cip->ci_wqes_alloced++;
                mutex_exit(&cip->ci_mutex);
        } else {
                mutex_enter(&altqp->isq_mutex);
                altqp->isq_wqes_alloced++;
                altqp->isq_rwqes_posted++;
                mutex_exit(&altqp->isq_mutex);
        }

        IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_post_recv_buffer_end,
            IBMF_TNF_TRACE, "", "ibmf_i_post_recv_buffer() exit\n");

        return (ret);
}

/*
 * ibmf_i_mgt_class_to_hdr_sz_off():
 *      Determine class header offser and size for management classes
 */
void
ibmf_i_mgt_class_to_hdr_sz_off(uint32_t mgt_class, uint32_t *szp,
    uint32_t *offp)
{
        uint32_t        hdr_sz = 0, hdr_off = 0;

        IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L4,
            ibmf_i_mgt_class_to_hdr_sz_off_start, IBMF_TNF_TRACE, "",
            "ibmf_i_mgt_class_to_hdr_sz_off(): mgt_class = 0x%x\n",
            tnf_uint, mgt_class, mgt_class);

        switch (mgt_class) {
        case MAD_MGMT_CLASS_SUBN_LID_ROUTED :
        case MAD_MGMT_CLASS_SUBN_DIRECT_ROUTE :
        case MAD_MGMT_CLASS_PERF :
        case MAD_MGMT_CLASS_BM :
        case MAD_MGMT_CLASS_DEV_MGT :
        case MAD_MGMT_CLASS_SNMP :
        case MAD_MGMT_CLASS_COMM_MGT:
                hdr_sz = IBMF_MAD_CL_HDR_SZ_1;
                hdr_off = IBMF_MAD_CL_HDR_OFF_1;
                break;
        case MAD_MGMT_CLASS_SUBN_ADM :
                hdr_sz = IBMF_MAD_CL_HDR_SZ_2;
                hdr_off = IBMF_MAD_CL_HDR_OFF_2;
                break;
        default:
                if (((mgt_class >= MAD_MGMT_CLASS_VENDOR_START) &&
                    (mgt_class <= MAD_MGMT_CLASS_VENDOR_END)) ||
                    ((mgt_class >= MAD_MGMT_CLASS_APPLICATION_START) &&
                    (mgt_class <= MAD_MGMT_CLASS_APPLICATION_END))) {
                        hdr_sz = IBMF_MAD_CL_HDR_SZ_3;
                        hdr_off = IBMF_MAD_CL_HDR_OFF_1;
                } else if ((mgt_class >= MAD_MGMT_CLASS_VENDOR2_START) &&
                    (mgt_class <= MAD_MGMT_CLASS_VENDOR2_END)) {
                        hdr_sz = IBMF_MAD_CL_HDR_SZ_4;
                        hdr_off = IBMF_MAD_CL_HDR_OFF_2;
                } else {
                        IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L4,
                            ibmf_i_mgt_class_to_hdr_sz_off_start,
                            IBMF_TNF_TRACE, "",
                            "ibmf_i_mgt_class_to_hdr_sz_off():"
                            "got illegal management class = 0x%x\n",
                            tnf_uint, mgt_class, mgt_class);
                }
                break;
        }



        *szp = hdr_sz;
        *offp = hdr_off;

        IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L4,
            ibmf_i_mgt_class_to_hdr_sz_off_end, IBMF_TNF_TRACE, "",
            "ibmf_i_mgt_class_to_hdr_sz_off() exit,hdr_sz = %d, hdr_off = %d\n",
            tnf_uint, hdr_sz, hdr_sz, tnf_uint, hdr_off, hdr_off);
}

/*
 * ibmf_i_lookup_client_by_mgmt_class():
 *      Lookup the client context based on the management class of
 *      the incoming packet
 */
int
ibmf_i_lookup_client_by_mgmt_class(ibmf_ci_t *ibmf_cip, int port_num,
    ibmf_client_type_t class, ibmf_client_t **clientpp)
{
        ibmf_client_t           *clientp;
        ibmf_client_info_t      *client_infop;

        IBMF_TRACE_3(IBMF_TNF_DEBUG, DPRINT_L4,
            ibmf_i_lookup_client_by_mgmt_class_start, IBMF_TNF_TRACE, "",
            "ibmf_i_lookup_client_by_mgmt_class() enter, cip = %p, "
            "port_num = %d, class = 0x%x\n", tnf_opaque, cip, ibmf_cip,
            tnf_int, port, port_num, tnf_opaque, class, class);

        ASSERT(MUTEX_NOT_HELD(&ibmf_cip->ci_clients_mutex));

        mutex_enter(&ibmf_cip->ci_clients_mutex);

        clientp = ibmf_cip->ci_clients;

        /* walk client context list looking for class/portnum match */
        while (clientp != NULL) {
                client_infop = &clientp->ic_client_info;
                if (class == client_infop->client_class &&
                    port_num == client_infop->port_num) {
                        /* found our match */
                        break;
                }
                clientp = clientp->ic_next;
        }

        mutex_exit(&ibmf_cip->ci_clients_mutex);

        if (clientp != NULL) {
                *clientpp = clientp;
                IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L4,
                    ibmf_i_lookup_client_by_mgmt_class_end, IBMF_TNF_TRACE, "",
                    "ibmf_i_lookup_client_by_mgmt_class() exit, clp = %p\n",
                    tnf_opaque, clientp, clientp);
                return (IBMF_SUCCESS);
        } else {
                IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
                    ibmf_i_lookup_client_by_mgmt_class_end, IBMF_TNF_TRACE, "",
                    "ibmf_i_lookup_client_by_mgmt_class() failure exit\n");
                return (IBMF_FAILURE);
        }
}

/*
 * ibmf_i_get_pkeyix():
 *      Get the pkey index of the pkey in the pkey table of the specified
 *      port. Take into account the partition membership.
 */
int
ibmf_i_get_pkeyix(ibt_hca_hdl_t hca_handle, ib_pkey_t pkey, uint8_t port,
    ib_pkey_t *pkeyixp)
{
        ib_pkey_t               tpkey;
        ibt_status_t            ibt_status;

        IBMF_TRACE_3(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_get_pkeyix_start,
            IBMF_TNF_TRACE, "", "ibmf_i_get_pkeyix() enter, hcahdl = %p, "
            "pkey = 0x%x, port = %d\n", tnf_opaque, hcahdl, hca_handle,
            tnf_int, pkey, pkey, tnf_int, port, port);

        /*
         * If the client specifies the FULL membership pkey and the
         * pkey is not in the table, this function should fail.
         */
        if (pkey & IBMF_PKEY_MEMBERSHIP_MASK) {
                ibt_status = ibt_pkey2index(hca_handle, port,
                    pkey, pkeyixp);
                if (ibt_status != IBT_SUCCESS) {
                        IBMF_TRACE_1(IBMF_TNF_NODEBUG, DPRINT_L1,
                            ibmf_i_get_pkeyix_err, IBMF_TNF_ERROR, "",
                            "ibmf_i_get_pkeyix() error status = %d\n",
                            tnf_uint, ibt_status, ibt_status);
                        IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
                            ibmf_i_get_pkeyix_end, IBMF_TNF_TRACE, "",
                            "ibmf_i_get_pkeyix() exit\n");
                        return (IBMF_TRANSPORT_FAILURE);
                }
                IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_get_pkeyix_end,
                    IBMF_TNF_TRACE, "", "ibmf_i_get_pkeyix() exit\n");
                return (IBMF_SUCCESS);
        }

        /*
         * Limited member pkey processing
         * Check if this limited member pkey is in the pkey table
         */
        ibt_status = ibt_pkey2index(hca_handle, port, pkey, pkeyixp);
        if (ibt_status == IBT_SUCCESS) {
                IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
                    ibmf_i_get_pkeyix_end, IBMF_TNF_TRACE, "",
                    "ibmf_i_get_pkeyix() exit\n");
                return (IBMF_SUCCESS);
        }

        /*
         * Could not find the limited member version of the pkey.
         * Now check if the full member version of the pkey is in the
         * pkey table. If not, fail the call.
         */
        tpkey = pkey | IBMF_PKEY_MEMBERSHIP_MASK;
        ibt_status = ibt_pkey2index(hca_handle, port, tpkey, pkeyixp);
        if (ibt_status != IBT_SUCCESS) {
                IBMF_TRACE_1(IBMF_TNF_NODEBUG, DPRINT_L1,
                    ibmf_i_get_pkeyix_err, IBMF_TNF_ERROR, "",
                    "ibmf_i_get_pkeyix() error status = %d\n",
                    tnf_uint, ibt_status, ibt_status);
                IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
                    ibmf_i_get_pkeyix_end, IBMF_TNF_TRACE, "",
                    "ibmf_i_get_pkeyix() exit\n");
                return (IBMF_TRANSPORT_FAILURE);
        }

        IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_get_pkeyix_end,
            IBMF_TNF_TRACE, "", "ibmf_i_get_pkeyix(): pkey_ix = %d\n",
            tnf_int, pkeyix, *pkeyixp);
        return (IBMF_SUCCESS);
}

/*
 * ibmf_i_pkey_ix_to_key():
 *      Figure out pkey from pkey index
 */
int
ibmf_i_pkey_ix_to_key(ibmf_ci_t *cip, uint_t port_num, uint_t pkey_ix,
    ib_pkey_t *pkeyp)
{
        ibt_status_t            ibt_status;

        IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_pkey_ix_to_key_start,
            IBMF_TNF_TRACE, "", "ibmf_i_pkey_ix_to_key() enter\n");

        ibt_status = ibt_index2pkey(cip->ci_ci_handle, port_num, pkey_ix,
            pkeyp);
        if (ibt_status != IBT_SUCCESS) {
                IBMF_TRACE_1(IBMF_TNF_NODEBUG, DPRINT_L1,
                    ibmf_i_pkey_ix_to_key, IBMF_TNF_TRACE, "",
                    "ibmf_i_pkey_ix_to_key(): ibt_index2pkey failed for "
                    " pkey index %d \n", tnf_uint, pkey_ix, pkey_ix);
                IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
                    ibmf_i_pkey_ix_to_key_end,
                    IBMF_TNF_TRACE, "", "ibmf_i_pkey_ix_to_key() exit\n");
                return (IBMF_TRANSPORT_FAILURE);
        }

        IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_pkey_ix_to_key_end,
            IBMF_TNF_TRACE, "", "ibmf_i_pkey_ix_to_key() exit\n");

        return (IBMF_SUCCESS);
}

/*
 * ibmf_i_ibt_to_ibmf_status():
 *      Map IBT return code to IBMF return code
 */
int
ibmf_i_ibt_to_ibmf_status(ibt_status_t ibt_status)
{
        int ibmf_status;

        IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_ibt_to_ibmf_status_start,
            IBMF_TNF_TRACE, "", "ibmf_i_ibt_to_ibmf_status() enter, "
            "status = %d\n", tnf_uint, ibt_status, ibt_status);

        switch (ibt_status) {

        case IBT_SUCCESS:
                ibmf_status = IBMF_SUCCESS;
                break;

        case IBT_INSUFF_KERNEL_RESOURCE:
        case IBT_INSUFF_RESOURCE:
        case IBT_QP_FULL:
                ibmf_status = IBMF_NO_RESOURCES;
                break;

        case IBT_HCA_IN_USE:
        case IBT_QP_IN_USE:
        case IBT_CQ_BUSY:
        case IBT_PD_IN_USE:
        case IBT_MR_IN_USE:
                ibmf_status = IBMF_BUSY;
                break;

        default:
                ibmf_status = IBMF_FAILURE;
                break;
        }

        IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_i_ibt_to_ibmf_status_end,
            IBMF_TNF_TRACE, "", "ibmf_i_ibt_to_ibmf_status() exit, "
            "ibt_status = %d, ibmf_status = %d\n", tnf_uint, ibt_status,
            ibt_status, tnf_int, ibmf_status, ibmf_status);

        return (ibmf_status);
}

/*
 * ibmf_i_ibt_wc_to_ibmf_status():
 *      Map work completion code to IBMF return code
 */
int
ibmf_i_ibt_wc_to_ibmf_status(ibt_wc_status_t ibt_wc_status)
{
        int ibmf_status;

        IBMF_TRACE_1(IBMF_TNF_DEBUG, DPRINT_L4,
            ibmf_i_ibt_wc_to_ibmf_status_start, IBMF_TNF_TRACE, "",
            "ibmf_i_ibt_to_ibmf_status() enter, status = %d\n",
            tnf_uint, ibt_wc_status, ibt_wc_status);

        switch (ibt_wc_status) {

        case IBT_WC_SUCCESS:
                ibmf_status = IBMF_SUCCESS;
                break;

        default:
                ibmf_status = IBMF_FAILURE;
                break;
        }

        IBMF_TRACE_2(IBMF_TNF_DEBUG, DPRINT_L4,
            ibmf_i_ibt_wc_to_ibmf_status_end, IBMF_TNF_TRACE, "",
            "ibmf_i_ibt_to_ibmf_status() exit, wc_status = %d, "
            "ibmf_status = %d\n", tnf_uint, ibt_wc_status,
            ibt_wc_status, tnf_int, ibmf_status, ibmf_status);

        return (ibmf_status);
}

/*
 * ibmf_i_is_ibmf_handle_valid():
 *      Validate the ibmf handle
 */
int
ibmf_i_is_ibmf_handle_valid(ibmf_handle_t ibmf_handle)
{
        ibmf_ci_t       *cip;
        ibmf_client_t   *clp, *clientp = (ibmf_client_t *)ibmf_handle;

        IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
            ibmf_i_is_ibmf_handle_valid_start, IBMF_TNF_TRACE, "",
            "ibmf_i_is_ibmf_handle_valid() enter\n");

        mutex_enter(&ibmf_statep->ibmf_mutex);

        cip = ibmf_statep->ibmf_ci_list;

        /* iterate through all the channel interace contexts */
        while (cip != NULL) {

                mutex_enter(&cip->ci_clients_mutex);

                clp = cip->ci_clients;

                /* search all registration contexts for this ci */
                while (clp != NULL) {
                        if (clp == clientp)
                                break;
                        clp = clp->ic_next;
                }

                mutex_exit(&cip->ci_clients_mutex);

                if (clp == clientp) {
                        /* ci found */
                        break;
                } else {
                        /* ci not found, move onto next ci */
                        cip = cip->ci_next;
                }
        }

        mutex_exit(&ibmf_statep->ibmf_mutex);

        if (cip != NULL) {
                IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
                    ibmf_i_is_ibmf_handle_valid_end, IBMF_TNF_TRACE, "",
                    "ibmf_i_is_ibmf_handle_valid() exit\n");
                return (IBMF_SUCCESS);
        } else {
                IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
                    ibmf_i_is_ibmf_handle_valid_end, IBMF_TNF_TRACE, "",
                    "ibmf_i_is_ibmf_handle_valid() failure exit\n");
                return (IBMF_FAILURE);
        }
}

/*
 * ibmf_i_is_qp_handle_valid():
 *      Validate the QP handle
 */
int
ibmf_i_is_qp_handle_valid(ibmf_handle_t ibmf_handle,
    ibmf_qp_handle_t ibmf_qp_handle)
{
        ibmf_client_t   *clientp = (ibmf_client_t *)ibmf_handle;
        ibmf_alt_qp_t   *alt_qp, *qpp = (ibmf_alt_qp_t *)ibmf_qp_handle;
        ibmf_ci_t       *cip = clientp->ic_myci;

        IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
            ibmf_i_is_qp_handle_valid_start, IBMF_TNF_TRACE, "",
            "ibmf_i_is_qp_handle_valid() enter\n");

        /* the default qp handle is always valid */
        if (ibmf_qp_handle == IBMF_QP_HANDLE_DEFAULT)
                return (IBMF_SUCCESS);

        mutex_enter(&cip->ci_mutex);

        alt_qp = cip->ci_alt_qp_list;

        while (alt_qp != NULL) {
                if (alt_qp == qpp) {
                        /* qp handle found */
                        break;
                } else {
                        /* qp handle not found, get next qp on list */
                        alt_qp = alt_qp->isq_next;
                }
        }

        mutex_exit(&cip->ci_mutex);

        if (alt_qp != NULL) {
                IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
                    ibmf_i_is_qp_handle_valid_end, IBMF_TNF_TRACE, "",
                    "ibmf_i_is_qp_handle_valid() exit\n");
                return (IBMF_SUCCESS);
        } else {
                IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
                    ibmf_i_is_qp_handle_valid_end, IBMF_TNF_TRACE, "",
                    "ibmf_i_is_qp_handle_valid() failure exit\n");
                return (IBMF_FAILURE);
        }
}

void
ibmf_dprintf(int l, const char *fmt, ...)
{
        va_list ap;

        if ((l) > ibmf_trace_level) {

                return;
        }

        va_start(ap, fmt);
        (void) vprintf(fmt, ap);
        va_end(ap);
}

/*
 * ibmf_setup_term_ctx():
 * Sets up a message context that is the duplicate of the one
 * passed in the regmsgimplp argument. The duplicate message context
 * is not visible to the client. It is managed internally by ibmf
 * to process the RMPP receiver termination flow logic for the
 * transaction while the client is notified of the completion of the
 * same transaction (i.e. all the solicited data has been received).
 */
int
ibmf_setup_term_ctx(ibmf_client_t *clientp, ibmf_msg_impl_t *regmsgimplp)
{
        ibmf_msg_impl_t *msgimplp;
        size_t          offset;
        uint32_t        cl_hdr_sz, cl_hdr_off;
        int             status;

        IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
            ibmf_setup_term_ctx_start, IBMF_TNF_TRACE, "",
            "ibmf_setup_term_ctx() enter\n");

        /*
         * Allocate the termination message context
         */
        msgimplp = (ibmf_msg_impl_t *)kmem_zalloc(sizeof (ibmf_msg_impl_t),
            KM_NOSLEEP);
        if (msgimplp == NULL) {
                IBMF_TRACE_1(IBMF_TNF_NODEBUG, DPRINT_L1,
                    ibmf_setup_term_ctx_error, IBMF_TNF_ERROR, "",
                    "ibmf_setup_term_ctx(): %s\n", tnf_string, msg,
                    "message mem allocation failure");
                IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
                    ibmf_setup_term_ctx_end, IBMF_TNF_TRACE, "",
                    "ibmf_setup_term_ctx() exit\n");
                return (IBMF_NO_RESOURCES);
        }

        _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*msgimplp))

        /* Copy the message context to the termination message structure */
        *msgimplp = *regmsgimplp;

        /* Initialize the message mutex */
        mutex_init(&msgimplp->im_mutex, NULL, MUTEX_DRIVER, NULL);

        /*
         * Allocate enough memory for the MAD header only.
         */
        msgimplp->im_msgbufs_recv.im_bufs_mad_hdr =
            (ib_mad_hdr_t *)kmem_zalloc(IBMF_MAD_SIZE, KM_NOSLEEP);
        if (msgimplp->im_msgbufs_recv.im_bufs_mad_hdr == NULL) {
                kmem_free(msgimplp, sizeof (ibmf_msg_impl_t));
                IBMF_TRACE_1(IBMF_TNF_NODEBUG, DPRINT_L1,
                    ibmf_setup_term_ctx_error, IBMF_TNF_ERROR, "",
                    "ibmf_setup_term_ctx(): %s\n", tnf_string, msg,
                    "recv buf mem allocation failure");
                IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
                    ibmf_setup_term_ctx_end, IBMF_TNF_TRACE, "",
                    "ibmf_setup_term_ctx() exit\n");
                return (IBMF_NO_RESOURCES);
        }

        /* Copy over just the MAD header contents */
        bcopy((const void *)regmsgimplp->im_msgbufs_recv.im_bufs_mad_hdr,
            (void *)msgimplp->im_msgbufs_recv.im_bufs_mad_hdr,
            sizeof (ib_mad_hdr_t));

        offset = sizeof (ib_mad_hdr_t);
        ibmf_i_mgt_class_to_hdr_sz_off(
            regmsgimplp->im_msgbufs_recv.im_bufs_mad_hdr->MgmtClass,
            &cl_hdr_sz, &cl_hdr_off);
        offset += cl_hdr_off;

        /*
         * Copy the management class header
         */
        msgimplp->im_msgbufs_recv.im_bufs_cl_hdr =
            (uchar_t *)msgimplp->im_msgbufs_recv.im_bufs_mad_hdr + offset;
        msgimplp->im_msgbufs_recv.im_bufs_cl_hdr_len =
            regmsgimplp->im_msgbufs_recv.im_bufs_cl_hdr_len;
        bcopy((void *)regmsgimplp->im_msgbufs_recv.im_bufs_cl_hdr,
            (void *)msgimplp->im_msgbufs_recv.im_bufs_cl_hdr,
            regmsgimplp->im_msgbufs_recv.im_bufs_cl_hdr_len);

        /*
         * Clear the termination message timers copied from the regular message
         * since ibmf_i_set_timer() expects them to be cleared.
         */
        msgimplp->im_rp_timeout_id = 0;
        msgimplp->im_tr_timeout_id = 0;

        /* Mark this message as being in a receiver RMPP mode */
        msgimplp->im_flags |= IBMF_MSG_FLAGS_RECV_RMPP;

        /* Mark this message as being a "termination flow" message */
        msgimplp->im_flags |= IBMF_MSG_FLAGS_TERMINATION;

        /*
         * Clear the IBMF_MSG_FLAGS_SET_TERMINATION copied over from the regular
         * message.
         */
        msgimplp->im_flags &= ~IBMF_MSG_FLAGS_SET_TERMINATION;

        /*
         * Clear the trans_state RECV_DONE and DONE flags so that the
         * protocol continues with the termination message context.
         */
        msgimplp->im_trans_state_flags &= ~IBMF_TRANS_STATE_FLAG_RECV_DONE;
        msgimplp->im_trans_state_flags &= ~IBMF_TRANS_STATE_FLAG_DONE;

        /* Clear out references to the old UD dest handles */
        msgimplp->im_ibmf_ud_dest = NULL;
        msgimplp->im_ud_dest = NULL;

        /*
         * Request new UD dest resources for the termination phase.
         * The old UD dest resources are freed when the IBMF client
         * calls ibmf_free_msg(), so they cannot be relied on to exist
         * when the RMPP termination loop completes.
         */
        status = ibmf_i_alloc_ud_dest(clientp, msgimplp, &msgimplp->im_ud_dest,
            B_FALSE);
        if (status != IBMF_SUCCESS) {
                kmem_free(msgimplp, sizeof (ibmf_msg_impl_t));
                IBMF_TRACE_2(IBMF_TNF_NODEBUG, DPRINT_L1,
                    ibmf_setup_term_ctx_err, IBMF_TNF_ERROR, "",
                    "ibmf_setup_term_ctx(): %s, status = %d\n",
                    tnf_string, msg, "UD destination resource allocation"
                    " failed", tnf_int, ibmf_status, status);
                IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4,
                    ibmf_setup_term_ctx_end, IBMF_TNF_TRACE, "",
                    "ibmf_setup_term_ctx() exit\n");
                return (status);
        }

        /*
         * Add the message to the termination client list by virtue of
         * having the IBMF_MSG_FLAGS_TERMINATION "im_flags" flag set.
         */
        ibmf_i_client_add_msg(clientp, msgimplp);

        /*
         * Increase the "allocted messages" count so that the client
         * does not unregister before this message has been freed.
         * This is necessary because we want the client context to
         * be around when the receive timeout expires for this termination
         * loop, otherwise the code will access freed memory and crash.
         */
        mutex_enter(&clientp->ic_mutex);
        clientp->ic_msgs_alloced++;
        mutex_exit(&clientp->ic_mutex);

        mutex_enter(&msgimplp->im_mutex);
        /* Set the response timer for the termination message. */
        ibmf_i_set_timer(ibmf_i_recv_timeout, msgimplp, IBMF_RESP_TIMER);
        mutex_exit(&msgimplp->im_mutex);

        IBMF_TRACE_0(IBMF_TNF_DEBUG, DPRINT_L4, ibmf_setup_term_ctx_end,
            IBMF_TNF_TRACE, "", "ibmf_setup_term_ctx() exit\n");

        return (IBMF_SUCCESS);
}